// 程序员面试100题之28字符串排列.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <string>//string.h does not work
using namespace std;
/*
void permution(string &str)//函数中只是输出结果,所以不改变值
{
// cout<<" str is "<<str<<endl; 这句能看出来程序思想错误,虽然确实每个分支都遍历了,但是根元素只进站出站一次,所以根元素只输出一次。递归本身的性质决定
size_t len = str.length();
if(len==0)
{
cout<<endl;
return;
}
string str2;// = new string is a points
for(size_t i=0;i<len;i++)
{
str2 = str;
cout<<str[i];
str.erase(i,1);
permution(str);
str = str2;// recovery
}
}
*/
static int sum=0;
/*permution2 计算不同元素的排列方式,当所有元素的不同时计算排列(全排列),当有相同的元素时,过滤掉相同的元素,相当于这些元素的不同排列个数(不同于组合)*/
void permution2(string &str,int k)//这思想来自于答案,交换第一个元素和其余的一个元素,使得每次调用之后第一个元素都不在相同,即a bcd, b acd, c bad,d bca. 这样当a 入栈时,函数调用bcd,b入栈时,函数调用acd,
{
//cout<<" str is "<<str<<endl;
size_t len = str.length();
if(k==len)
{
for (int j=0;j<len;j++)
{
cout<<str[j];// 并没有删除和添加元素,而是在原来数组的基础上处理,所以才能把原来的值都取出来
}
cout<<endl;sum++;//str2="";
return;
}
string str2="";// = new string is a points,should be placed here, create in every
char ch_temp;
for(size_t i=k;i<len;i++)
{
ch_temp = str[i];
str[i] = str[k];
str[k] = ch_temp;
for (int i1=0;i1<str2.length();i1++)//here judge
{
if (str2[i1]==str[k])//already exist
{
ch_temp = str[i];//
str[i] = str[k];
str[k] = ch_temp;
//if (k!=0)
//{
// return;// here is wrong, when return 主函数只执行一次就被返回,主函数的for循环没执行
//}
//else
goto label;//break;
}
}
str2.push_back(str[k]);// 添加到str2 然后判断是否已经存在
//? cout<<str[k];//这样输出还是不好使
permution2(str,k+1);
ch_temp = str[i];
str[i] = str[k];
str[k] = ch_temp;
label:;//empty sentence
}
}
上面是我的代码,下面是别人的代码,看到后自惭形愧呀!
bool IsSwap(char* pBegin , char* pEnd) //在[nBegin,nEnd)区间中是否有字符与下标为pEnd的字符相等
{
char *p;
for(p = pBegin ; p < pEnd ; p++)
{
if(*p == *pEnd)
return false;
}
return true;
}
void Permutation(char* pStr , char *pBegin)
{
if(*pBegin == '\0')
{
static int num = 1; //局部静态变量,用来统计全排列的个数
printf("第%d个排列\t%s\n",num++,pStr);
}
else
{
for(char *pCh = pBegin; *pCh != '\0'; pCh++) //第pBegin个数分别与它后面的数字交换就能得到新的排列
{
if(IsSwap(pBegin , pCh))
//if(i==0||pBegin != pCh)// the first exchanged even same, this is wrong
{
swap(*pBegin , *pCh);
Permutation(pStr , pBegin + 1);
swap(*pBegin , *pCh);
}
}
}
}
一直不是道自己有多笨,有多差,想了很久才看明白别人的代码。起始就是根据排列的定义,一个元素接一个元素的选择,对于有相同的元素,则当前位置的选择跳过已经选择过的元素,也就是上一步的IsWap()函数。假如当前的位置为pBegin,因为pBegin前的位置都已经选择好了,于是判断当前位置开始之后的所有元素pCh那些能够放在当前位置,如果能,则交换pCh和pBegin,然后判断下一个位置(即递归)。例如aabcc,当pBegin=0时,能选择的元素位置为0,2,3.只有三个序列后进入下一层递归,aabcc,baacc,cabac。进一步,假设aabcc序列进入下一层递归,pBegin=1,第一个位置为a固定了,此时第二个位置能选择的依然只有三个元素,生成的可以进入下一层递归的序列为abcc,bacc,cbac,注意不能有cbca序列进入下一层,因为当pCh指向第二个c时,已经选择过第一个c了,IsWap()函数不成立。以此类推。我不知道自己写了这么多乱码七糟的东西,只是感觉自己掉进了这个漩涡里,花了很久才出来。本博客只作为自己学习札记,若您浪费了时间读到此处,并对我的愚昧颇感吃惊,我将不胜荣幸!
void Combination(char *string ,int number,vector<char> &result);
void Combination1(char *string)
{
assert(string != NULL);
vector<char> result;
int i , length = strlen(string);
for(i = 1 ; i <= length ; ++i)
Combination(string , i ,result);
}
void Combination(char *string ,int number , vector<char> &result)
{
assert(string != NULL);
if(number == 0)
{
static int num = 1;
printf("第%d个组合\t",num++);
vector<char>::iterator iter = result.begin();
for( ; iter != result.end() ; ++iter)
printf("%c",*iter);
printf("\n");
return ;
}
if(*string == '\0')
return ;
result.push_back(*string); // 对于重复元素,可以在进入容器之前,判断是够存在,若存在则不进入容器
Combination(string + 1 , number - 1 , result);
result.pop_back();
Combination(string + 1 , number , result);
}
组合