字符串的全排列。。。
打印字符串的所有排列。例如,若输入为abc,则打印出acb、bac、cba、cab、bca、abc。。。。
void AllPermutate(char a[], int num,int first)
{
if(first == (num-1) )
{
copy(a, a + num,ostream_iterator<char>(cout, " "));
cout << endl;
}
else
{
char temp;
for(inti = first; i < num; i++)
{
if(a[i] == a[first] && first !=i) //avoid genearate duplicate..
continue;
temp=a[i];
a[i]=a[first];
a[first]=temp;
AllPermutate(a, num, first + 1);
temp=a[i];
a[i]=a[first];
a[first]=temp; //backtrace
}
}
}
把升序的排列(当然,也可以实现为降序)作为当前排列开始,然后依次计算当前排列的下一个字典序排列。
对当前排列从后向前扫描,找到一对为升序的相邻元素,记为i和j(i < j)。如果不存在这样一对为升序的相邻元素,则所有排列均已找到,算法结束;否则,重新对当前排列从后向前扫描,找到第一个大于i的元素k,交换i和k,然后对从j开始到结束的子序列反转,则此时得到的新排列就为下一个字典序排列。这种方式实现得到的所有排列是按字典序有序的,这也是C++ STL算法next_permutation的思想。参考JULY的博客,算法实现如下:
template <typename T>
void CalcAllPermutation(T perm[],int num)
{
if (num < 1)
return;
while (true) {
int i;
for (i = num - 2; i >= 0; --i){
if (perm[i] < perm[i + 1])
break;
}
if (i < 0)
break; // 已经找到所有排列
int k;
for (k = num - 1; k > i; --k) {
if (perm[k] > perm[i])
break;
}
swap(perm[i], perm[k]);
reverse(perm + i + 1, perm + num);
}
}
分析:这个方法是康托展开的应用。
何为康托展开?
举例:找出45231在12345所有字典序排列中的顺序?
比4小的数有3个
比5小的数有4个但4已经在之前出现过了所以是3个
比2小的数有1个
比3小的数有两个但2已经在之前出现过了所以是1个
比1小的数有0个
那么45231在这个排列中的顺序是3*4!+3*3!+1*2!+1*1!+0*0!+1=94
明白了其原理,就可以得到康托展开的表达式:
X=an*(n-1)!+a(n-1)*(n-2)!+…+ai*(i-1)!+…+a2*1!+a1*0!其中,a[m]代表比在第m位的数字小并且没有在第m位之前出现过的数字的个数, x代表比这个数小的数的个数,所以这个数的顺序就是x+1
我们就可以通过这一性质解释上述的算法:
· 从后往前找,直到找到序列第一次下降为止,找到k,在第i位。
· 从该位置向后找,找到最小一个比改为数字大的数字,找到m,在第j位。
· 交换k,m这两个数字。
· 将排列的第i+1位到末尾倒转,即得到了下一个排列。
拿14253做例子:
· 找到第一个下降的数字:2,在第3位。
· 找到第4位及以后的数字中最小的,但比2大的数字:3,在第5位。
· 交换2和3,重新得到排列 14352。
· 将第4位到第5位的数字倒转 得到下一个排列 14325。
· 算法结束。
算法的正确性:
考虑当前位的康托表达式X=an*(n-1)!+a(n-1)*(n-2)!+...+ai*(i-1)!+...+a2*1!+a1*0!
我们证明算法生成的排列的表达式为X'-X=1:
首先,算法并未改变i位以前的任何数,故x与x'的an~a[i+1]的值一样;
对于第i位之后的数,由于其单调下降,把i和j位的数调转后ai+=1,a(j)不变
(k<m,但原本k在m前,但现在k在m后,故ai=(aj)原+1,又由于区间[i-1,j+1]中的数均>J>I,故(ai)原=(aj)原)
此时[i-1,1]中的数也是单调下降的,将其掉转后变为单调上升,a'[i-1]=a'[i-2]=...=a'[1]=0;
故可以计算出X'-X=(i-1)!-(a[i-1]*(i-2)!+a[i-2]*(i-1)!+...+a[1]*(1-1)!);由于在原排列中[i-1,1]单调下降,故a[1],...,a[i-1],组成一个首项为0,公差为1,项数为i-1的等差数列,得a[i]=i-1;
又有:(i-1)!=(i-2)!+(i-2)*(i-2)!
=(i-3)!+(i-3)*(i-3)!+(i-2)*(i-2)!
=...
=0!+∑(i=0..i-2):i*i!=1+(a[i-1]*(i-2)!+a[i-2]*(i-1)!+...+a[1]*(1-1)!)
所以,X'-X=1;
字符串的全组合。。。
打印字符串(无重复字符)的所有组合。例如,若输入为abc,则打印出a、b、c、ab、ac、bc、abc。仅字符顺序不同而字符组成相同者,视为无差别,如ab与ba被认为无区别。一般地,若字符串的长度为N,则输出项数为N取1、N取2、... N取N一系列组合数之和。各输出字符串的相对顺序不限。
//方法一。
/***********************************************************************************
*从字符串的后面开始扫描,把结果保存到result中。当往result中加入新的字符时,
*它与result中已存在的每一个字符组合成新的字符加入到result中。
***********************************************************************************/
typedef vector<string>strVector;
void doCombine(const string&original, strVector &result_container, unsigned offset) {
if (offset >= original.size())
return;
doCombine(original, result_container, offset+1);
const unsigned nCount = result_container.size();
result_container.push_back(string(1, original[offset]));
for (unsigned index = 0; index < nCount; ++index) {
string temp(1, original[offset]);
temp.append(result_container[index]);
result_container.push_back(temp);
}
}
void combine(const char *szStr){
const string original(szStr);
vector<string> container;
if (!original.size())
return;
doCombine(original, container, 0);
for (unsigned index = 0; index < container.size(); ++index) {
cout<< container[index].c_str() <<endl;
}
}
//方法二。
/************************************************************************************
*从字符串的前面开始扫描,取出第一个字符,与剩余的字符相组合。
*然后回溯之。如'abc',-->'a', 'ab', 'abc', 'abcd', 'abd', 'ac'.....
************************************************************************************/
void str_doCombine(const string&original, string &result, unsigned offset) {
const unsigned nLength = original.size();
if (offset >= nLength)
return;
for (unsigned index = offset; index <nLength; ++index) {
result.append(1, original[index]);
cout << result.c_str() <<endl;
str_doCombine(original, result, index +1);
result.resize(result.size() - 1); //backtrace
}
}
void str_combine(const char *szStr){
string original(szStr);
string result;
if (!original.size())
return;
str_doCombine(original, result, 0);
}
参考文献:
http://www.cnblogs.com/sujz/archive/2011/06/16/2082831.html
http://blog.youkuaiyun.com/ly92are1999/article/details/6590802
http://blog.youkuaiyun.com/v_july_v/article/details/6879101
http://www.yuxingzhou.com/?p=99