字符串的全排列和全组合
来源:互联网 发布:水地暖 电地暖 知乎 编辑:程序博客网 时间:2024/04/29 18:38
字符串的全排列。。。
打印字符串的所有排列。例如,若输入为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.csdn.net/ly92are1999/article/details/6590802
http://blog.csdn.net/v_july_v/article/details/6879101
http://www.yuxingzhou.com/?p=99
- 字符串的全排列和全组合
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- [收集]字符串的全排列和组合
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- 字符串的全排列和组合算法
- mysql中查询多条不重复记录值的解决办法
- 由拖库攻击谈口令字段的加密策略
- Android的Handler总结
- v1.6+++++++带来的---如何配置vxworks的BSP使其正常引导bootrom/vxworks
- C语言练级笔记:Printf
- 字符串的全排列和全组合
- 模型间的一对多关系
- android权限总结
- 2D Application
- eclipse快捷键
- 二叉排序树
- 我国历史上经历了哪些主要朝代,各有多少年?
- Android 编译系统分析
- 日子只能往前走,一个方向顺时钟