字符串排列组合问题
一.全排列无重复字符
#include<stdio.h> #include<string.h> char buf[1024]; void func(int index); void swap(int index1,int index2); int main() { memset(buf,0,sizeof(buf)); sprintf(buf,"abc"); func(0); return 0; } void func(int index) { if(index == strlen(buf)-1) { printf("%s\n",buf); } else { for(int i=index;i<strlen(buf);i++)//注意这里i是从index开始的,这里做的决策是每个剩余的字符都可能做index位 { swap(index,i); func(index+1); swap(index,i); } } } void swap(int index1,int index2) { char temp = buf[index1]; buf[index1] = buf[index2]; buf[index2] = temp; }
二.全排列有重复字符
由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这二个数就不交换了。如122,第一个数与后面交换得212、221。然后122中第二数就不用与第三个数交换了,但对212,它第二个数与第三个数是不相同的,交换之后得到221。与由122中第一个数与第三个数交换所得的221重复了。所以这个方法不行。
换种思维,对122,第一个数1与第二个数2交换得到212,然后考虑第一个数1与第三个数2交换,此时由于第三个数等于第二个数,所以第一个数不再与第三个数交换。再考虑212,它的第二个数与第三个数交换可以得到解决221。此时全排列生成完毕。
这样我们也得到了在全排列中去掉重复的规则——去重的全排列就是从第一个数字起每个数分别与它后面非重复出现的数字交换。
#include<stdio.h> #include<string.h> char buf[1024]; void func(int index); void swap(int index1,int index2); bool isSwap(int index1,int index2); int main() { memset(buf,0,sizeof(buf)); sprintf(buf,"abb"); func(0); return 0; } void func(int index) { if(index == strlen(buf)-1) { printf("%s\n",buf); } else { for(int i=index;i<strlen(buf);i++) { if(isSwap(index,i)) { swap(index,i); func(index+1); swap(index,i); } } } } void swap(int index1,int index2) { char temp = buf[index1]; buf[index1] = buf[index2]; buf[index2] = temp; } bool isSwap(int index1,int index2)//判断前面是否已经有与buf[index2]相同的字符 { if(index1 == index2) return true; else { for(int i=index1;i<index2;i++) { if(buf[i] == buf[index2]) { return false; } } return true; } }
三.从m个字符中取n个组合,如abc的组合有a、bc、ab、ac、bc、abc
方法一:利用位运算来实现求组合,例如:对于字符序列“abc”,让其与1-7的二进制相与,每次输出对应二进制为1的那几位,即可输出全部组合。
#include<stdio.h> #include<string.h> char buf[1024]; void func(); int main() { memset(buf,0,sizeof(buf)); sprintf(buf,"abb"); func(); return 0; } void func() { int n = strlen(buf); if(n > 31) { return; } for(int i=1;i<(1<<n);i++) { for(int j=0;j<n;j++) { if((1<<j)&i) { printf("%c",buf[j]); } } printf("\n"); } }
方法二:先决定选几个字符,再遍历字符串,依次做两种决策:第i个字符在结果中;第i个字符不在结果中,最后看字符数是否与开始选定的值相等
代码略
四.八皇后问题
题目:在8×8的国际象棋上摆放八个皇后,使其不能相互攻击,即任意两个皇后不得处在同一行、同一列或者同一对角斜线上。下图中的每个黑色格子表示一个皇后,这就是一种符合条件的摆放方法。请求出总共有多少种摆法。
这就是有名的八皇后问题。解决这个问题通常需要用递归,而递归对编程能力的要求比较高。因此有不少面试官青睐这个题目,用来考察应聘者的分析复杂问题的能力以及编程的能力。
由于八个皇后的任意两个不能处在同一行,那么这肯定是每一个皇后占据一行。于是我们可以定义一个数组ColumnIndex[8],数组中第i个数字表示位于第i行的皇后的列号。先把ColumnIndex的八个数字分别用0-7初始化,接下来我们要做的事情就是对数组ColumnIndex做全排列。由于我们是用不同的数字初始化数组中的数字,因此任意两个皇后肯定不同列。我们只需要判断得到的每一个排列对应的八个皇后是不是在同一对角斜线上,也就是数组的两个下标i和j,是不是i-j==ColumnIndex[i]-Column[j]或者j-i==ColumnIndex[i]-ColumnIndex[j]。
#include<stdio.h> #include<string.h> int buf[8]; void func(int index); void swap(int index1,int index2); bool isLegal(); int main() { memset(buf,0,sizeof(buf)); for(int i=0;i<8;i++) { buf[i] = i; } func(0); return 0; } void func(int index) { if(index == 7) { if(isLegal()) { for(int i=0;i<7;i++) { printf("%d,",buf[i]); } printf("%d\n",buf[7]); } } for(int i=index;i<8;i++) { swap(i,index); func(index+1); swap(i,index); } } void swap(int index1,int index2) { if(index1==index2) { return; } else { int temp = buf[index1]; buf[index1] = buf[index2]; buf[index2] = temp; } } bool isLegal() { for(int i=0;i<7;i++) { for(int j=i+1;j<8;j++) { if((i-j) == (buf[i]-buf[j]) || (i-j) == (buf[j]-buf[i])) { return false; } } } return true; }