在最近的一次采访中,我被问到一个非常简单的问题,即在没有任何额外变量和任何内置函数的情况下反转字符串(不仅仅是打印)。我能想到的最接近的是:
<div id="trex-widget391" class="trex-popup-widget"></div>
<script>
function trexCallback391(trex){document.getElementById("trex-widget391").innerHTML=trex.html;}
jQuery(".trex-popup-widget").mouseover(function(e){
if(!sessionStorage.seenPopup) {
jQuery("#trex_overlay_fsl_popup").fadeIn('slow');
sessionStorage.seenPopup = "yes";
}
});
</script>
<script src="http://clanci.geek.hr/widget/widget.php?id=391" async defer></script>
我使用变量#include<stdio.h>
#include<string.h>
int main()
{
char ch[100];
scanf("%s",&ch);
int i=0;
while(i<strlen(ch)/2)
{
ch[i]=ch[strlen(ch)-1-i]+ch[i];
ch[strlen(ch)-1-i]=ch[i]-ch[strlen(ch)-1-i];
ch[i]=ch[i]-ch[strlen(ch)-1-i];
i++;
}
printf("%s",ch);
return 0;
}
拒绝了我的解决方案。如果不使用计数器变量,这怎么可能呢?有没有其他方法可以解决这个问题?
修改
这些是确切的问题(无论或多或少):
在不使用C中的任何变量或内置函数的情况下反转字符串。
答案 0 :(得分:5)
两个三种可能的实现:一种只反向打印字符串。另一个反转字符串在内存和就地。两者都假设允许定义自己的递归函数,并且参数不计为变量。在任何情况下,参数本身都是const
ant,因此可以说不是变量。
void printRev(const char * const s){
if(*s != '\0'){ // or just: if(*s){
printRev(s + 1);
putchar(*s);
}
}
是否通过字符串'前缀'递归:首先递归直到到达结尾,然后在递归调用返回后打印每个字符。
void revStr(char * const s, const int len){
if(len > 0){
if(s[0] != s[len]){
s[0] ^= s[len];
s[len] ^= s[0];
s[0] ^= s[len];
}
revStr(s + 1, len - 2);
}
}
稍微复杂一点:XOR-swaps字符串的'first'字符带'last'。然后使用下一个字符作为字符串的开头进行递归,并且长度减少2。因此,在下一次迭代中,第二个字符成为第一个字符,倒数第二个字符成为最后一个字符。为此,s
指针本身仍为const
,但显然指向的字符已被修改。
第二个函数需要字符串长度作为输入参数,这也可以(递归地)完成,而不需要内置的strlen
函数:
int myStrlen(const char * const s){
if(*s != '\0'){
return 1 + myStrlen(s + 1);
}
return 0;
}
<强> ADDIT 强>
这是一个不使用长度参数的版本,但需要一个不相交的输出字符串,并且输入字符串是可修改的。它通过使用NUL字符替换len - 2
中的最后一个字符来模拟revStr
中的src
表达式。
void copyRev(char * const restrict dst, char * const restrict src){
if(src[0] != '\0'){
dst[0] = src[myStrlen(src) - 1];
dst[myStrlen(src) - 1] = src[0];
src[myStrlen(src) - 1] = '\0';
copyRev(dst + 1, src + 1);
}
}
答案 1 :(得分:2)
我们可以重新分配字符串内存以使其大小加倍 然后我们可以将字符串memcpy到下半部分。
e.g。 S T R \ 0 S T R \ 0
现在我们可以一次将1个字符记忆到下半年的原始字符串。
这假设一个ascii字符串,其中'\ 0'终止字符。
void reverseString(const char** pStr)
{
*pStr = realloc(*pStr, strlen(*pStr)*2 + 2);
memcpy(*pStr + strlen(*pStr), *pStr, strlen(*pStr));
while (*pStr)
{
memcpy(*pStr, *pStr + strlen(*pStr + 2), 1);
++ (*pStr);
}
}
答案 2 :(得分:0)
使用C11的能力创建复合文字作为参数并返回值:
typedef struct foo_T {
char *s;
char *end;
char left, right;
} foo_T;
foo_T foo(const foo_T f) { // No variables, just constant parameter
if (f.s < f.end) {
*f.s = f.right;
*f.end = f.left;
return (foo_T){f.s+1,f.end-1,*f.s,*f.end};
}
return f;
}
char *endptr(char * const s) { // No variables, just constant parameter
if (*s == 0) return s-1;
return endptr(s + 1);
}
void rev_novar(char *s) {
if (*s && s[1]) {
foo ((foo_T){s,endptr(s),*s,*endptr(s)});
}
}
测试
void test_rev(const char *s) {
char buf[100];
memset(buf, 77, sizeof buf);
strcpy(&buf[1], s);
printf("%d '%s' %d\n", buf[0], &buf[1], buf[strlen(buf) + 1]);
rev_novar(&buf[1]);
printf("%d '%s' %d\n\n", buf[0], &buf[1], buf[strlen(buf) + 1]);
}
int main(void) {
test_rev("123");
test_rev("1234");
test_rev("1");
test_rev("");
return 0;
}
测试结果。 (77是左右哨兵。)
77 '123' 77
77 '321' 77
77 '1234' 77
77 '4231' 77
77 '1' 77
77 '1' 77
77 '' 77
77 '' 77
答案 3 :(得分:0)
我的猜测是面试官错了。如果没有任何变量,您可以使用一系列硬编码的if语句来查找字符串的结尾,然后使用硬编码的交换序列。它在概念上与排序网络类似,排序网络用于排序固定数量的值(最多16个,基于维基文章)。例如,对于长度为5的字符串:
void reverse(char str[])
{
if(str[0] == 0 || str[1] == 0)
return;
if(str[2] == 0){
str[0] ^= str[1]; str[1] ^= str[0]; str[0] ^= str[1];
return;
}
if(str[3] == 0){
str[0] ^= str[2]; str[2] ^= str[0]; str[0] ^= str[2];
return;
}
if(str[4] == 0){
str[0] ^= str[3]; str[3] ^= str[0]; str[0] ^= str[3];
str[1] ^= str[2]; str[2] ^= str[1]; str[1] ^= str[2];
return;
}
if(str[5] == 0){
str[0] ^= str[4]; str[4] ^= str[0]; str[0] ^= str[4];
str[1] ^= str[3]; str[3] ^= str[1]; str[1] ^= str[3];
return;
}
}
即使使用上面的代码,XOR操作也需要一个内部变量(寄存器),除非处理器有一个xor内存到内存指令,(比较为零可以在X86 / X64处理器上使用立即数)。
示例代码,如果允许常量指针和递归,但这实际上使用堆栈作为变量的多个实例(指针),因为它们被操作为指针+ 1和/或指针-1,并且因为reverse1()是tail递归,优化编译器可能会将其转换为循环,将beg和end作为常规变量。
void reverse0(char * const beg, char * const end);
void reverse1(char * const beg, char * const end);
/* entry function */
void reverse(char str[])
{
if(str[0] == 0 || str[1] == 0)
return;
reverse0(&str[0], &str[2]);
}
/* find end of str */
void reverse0(char * const beg, char * const end)
{
if(*end != 0){
reverse0(beg, end+1);
return;
}
reverse1(beg, end-1);
}
/* reverse str */
void reverse1(char * const beg, char * const end)
{
*beg ^= *end;
*end ^= *beg;
*beg ^= *end;
if((beg+1) < (end-1))
reverse1(beg+1, end-1);
}