我的任务是代码将字符串作为输入,如果字符串中有x,则用0或1替换它们,并打印出所有可能的字符串组合。我们也必须使用递归。例如,如果输入字符串为“1x0X”,则输出为:
1000
1001
1100
1101
我真的在努力学习如何在没有完整字符串的情况下找到字符串的所有排列。我有一系列的功能结合起来打印出一系列数字的所有排列,但我不知道如何创建一个只能置换列表中某些元素的函数。
有没有人对如何做到这一点有任何建议?
答案 0 :(得分:2)
当递归深度有限且不太大时,最好使用递归。
然而,抛开这个公理,下面是一个双重递归解决方案。它快速占用堆栈空间(这是它最大的限制因素),因此不是一个强大的解决方案。但它得到了这份工作。
对于"代码将字符串作为输入,",使用fgets()
- 未显示。
然而,在递归的精神中,为什么不递归输入呢? print_combo()
递归生成一个字符链表(LL),并跟踪'x'
读取的数量。一旦发生行尾/文件结束,就可以打印,链表从最后一个字符开始。
foo()
递归以相反的顺序打印LL,传入二进制掩码以指示x
或0
的{{1}}替换。 1
二进制掩码通常适用于32 unsigned
。这是另一个限制。
如果必须,请将鼠标悬停在代码上。
x
typedef struct node { const struct node *prev; int ch; } node; // Print the line void foo(const node *prev, unsigned mask) {
if (prev) {
if (prev->ch == 'x' || prev->ch == 'X') {
foo(prev->prev, mask >> 1);
putchar("01"[mask & 1]);
} else {
foo(prev->prev, mask);
putchar(prev->ch);
}
}
}
// Read, form the LL and then print void print_combo(const node *prev, unsigned xcount) {
node n = {.prev = prev, .ch = getchar()};
if (n.ch == '\n' || n.ch == EOF) {
for (unsigned mask = 0; mask < (1u << xcount); mask++) {
foo(prev, mask);
putchar('\n');
}
} else {
print_combo(&n, xcount + (n.ch == 'x' || n.ch == 'X'));
}
}
输入
int main(void) {
print_combo(NULL, 0);
}
输出
00x01x10x11
答案 1 :(得分:2)
此代码基本上逐字实现了我在comment中建议的内容。它接受x
或X
作为有效标记,因为问题中的示例也是如此。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void map_x(const char *str)
{
size_t xloc = strcspn(str, "xX"); if (str[xloc] == '\0') printf("%s\n", str); else { char *copy = strdup(str); copy[xloc] = '0'; map_x(copy); copy[xloc] = '1'; map_x(copy); free(copy); }
}
int main(void)
{
char buffer[4096];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
map_x(buffer);
}
return 0;
}
main()
函数在所有三种变体中基本相同。使用strcspn()
是一种标准习惯用法,它可以修剪从第一个换行符开始的所有内容,或者如果没有换行符,则覆盖字符串的结尾。
请注意,即使将只读字符串文字传递给函数,此解决方案也是安全的。它不会修改传递的字符串。如果初始字符串实际上是只读字符串文字,则以下解决方案将崩溃或以其他方式失败。
可以确定字符串长度,分配VLA(可变长度数组)以获取字符串副本,并将字符串复制到VLA中。这将大大降低为字符串分配内存的成本(VLA分配比通用内存分配器简单得多)。
此代码实现了Gene中comment建议的内容。它会更高效,因为它没有额外的内存分配,在大多数系统上都是昂贵的操作。
#include <stdio.h>
#include <string.h>
static void map_x(char *str)
{
size_t xloc = strcspn(str, "xX"); if (str[xloc] == '\0') printf("%s\n", str); else { char letter = str[xloc]; str[xloc] = '0'; map_x(str); str[xloc] = '1'; map_x(str); str[xloc] = letter; }
}
int main(void)
{
char buffer[4096];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
map_x(buffer);
}
return 0;
}
这可以通过不重新扫描已知没有x
的前缀来优化工作。
/* SO 4764-4683 */
#include <stdio.h>
#include <string.h>
static void map_x(char *str, size_t offset)
{
size_t xloc = strcspn(&str[offset], "xX") + offset; if (str[xloc] == '\0') printf("%s\n", str); else { char letter = str[xloc]; str[xloc] = '0'; map_x(str, xloc); str[xloc] = '1'; map_x(str, xloc); str[xloc] = letter; }
}
int main(void)
{
char buffer[4096];
while (fgets(buffer, sizeof(buffer), stdin) != 0)
{
buffer[strcspn(buffer, "\n")] = '\0';
map_x(buffer, 0);
}
return 0;
}
几乎所有输入都可能无法衡量性能差异,因为I / O时间占主导地位。
答案 2 :(得分:1)
我会做一些更简单的事情。只需使用position参数迭代输入字符串。每当你点击'x'字符两次递归时,一次为'0',一次为'1'。返回后,请务必将字符重置为“x”。每当你点击数字字符只需递归一次。每次递增时递增位置参数。当您点击字符串的末尾时,将其打印出来。有了这个想法,你会得到这样的东西:
#include <stdio.h>
void print_combo(char *str, int pos) {
char c;
c = str[pos];
switch (c) {
case '0':
case '1':
print_combo(str, pos+1);
break;
case 'x':
case 'X':
str[pos] = '0';
print_combo(str, pos+1);
str[pos] = '1';
print_combo(str, pos+1);
str[pos] = c;
break;
case '\0':
printf("%s\n", str);
break;
default:
printf("bad input\n");
break;
}
}
int main() {
char str[10];
strcpy(str, "1x0x");
printf("printing %s\n", str);
print_combo(str, 0);
strcpy(str, "0x01x");
printf("printing %s\n", str);
print_combo(str, 0);
strcpy(str, "0x01x0X1");
printf("printing %s\n", str);
print_combo(str, 0);
return 0;
}
我的输出如下:
printing 1x0x
1000
1001
1100
1101
printing 0x01x
00010
00011
01010
01011
printing 0x01x0X1
00010001
00010011
00011001
00011011
01010001
01010011
01011001
01011011