剑指offer二十七--字符串的排列
1.题目描述
1.1题目要求
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
1.2输入描述
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
2.解决思路
2.1递归思想
2.1.1思路
先简化问题假设这n个字符不重复
大问题 n个字符全排列 =》 让n个字符依次做第一个字符,每次固定后 + 后n-1个字符全排列
举例:abc排列 =》 固定a bc排列 、 固定b ac排列 、 固定c ba排列
基线条件(递归结束条件): n个字符都排好了
递归条件(递归继续条件): 排好的字符个数小于n
递归体: for(int i = start;i++;i<=n){ 1.固定start 2.递归自身排后面的字符}
注:固定start,让n个字符依次做第一个字符,通过和第一个字符交换位置来实void Swap(char &a, char &b) {char tmp = a;
a = b; b = tmp; } void PaiLie(char a[], int start, int len, vector<string> &result) { if (start == len - 1) { result.push_back(a); Display(a, len); return; } for (int i = start; i < len; i++) { Swap(a[start], a[i]); PaiLie(a, start + 1, len, result);
Swap(a[start], a[i]); // 交换回去因为数组是引用传递,下面的递归交换顺序会影响上一层递归,所以每一层交换要交换回去
} }
然后进一步考虑重复字符的情况,按上面的思路处理,
例如 abb =》 1固定a 排bb 、2固定b 排b ab、3固定 b 排ba
aab =》 1固定a 排ab 2固定a 排 ab 3固定b 排aa
可见 问题一:abb 2、3重复,其原因是a与相同元素交换了2次,
问题二:aab 1、2重复,原因是a与另一个a交换了,其后n-1个元素相同
综上,依次让n个字符做第一个字符,不能重复。
递归体做如下变换:
for(int i = start;i++;i<=n){ 1.只要出现过start位置就不再出现,通过一个set保证 2.递归自身排后面的字符}
2.1.2递归最终代码
#include<vector> #include<string.h> #include <iostream> #include<algorithm> #include<set> using namespace std; class Solution { public: void Swap(char &a, char &b) { char tmp = a; a = b; b = tmp; } void PaiLie(char a[], int start, int len, vector<string> &result) { if (start == len - 1) { result.push_back(a); return; } set<char> is_apper; for (int i = start; i < len; i++) { if (is_apper.find(a[i]) == is_apper.end()) {// 如果从没有出现start位置,那就去走递归体 is_apper.insert(a[i]); // 记录a[i]出现过start位置 Swap(a[start], a[i]); PaiLie(a, start + 1, len, result); Swap(a[start], a[i]); // 恢复状态 } } } vector<string> Permutation(string str) { vector<string> result; char *a = new char[str.size()]; strcpy(a, str.c_str()); // 将string转为char PaiLie(a, 0, str.size(), result); sort(result.begin(), result.end());// 保证字典序,即result里的东西有序 return result; } };
2.2字典序解法
2.2.1思路
abc的答案 abc、acb、bac、bca、cab、cba;知道abc能否推知下一个排序呢?
字典序法就是这样,按规律推出下一中排列的可能。
字典序算法的步骤如下:
1、从原排列中,从右至左,找到第一个左邻小于右邻的字符,记左邻位置为 a。
2、重新从右至做,找到第一个比 list[a] 大的字符,记为位置为 b。
3、交换 a 和 b 两个位置的值。
4、将 a 后面的数,由小到大排列。
通过找13254的下一个排列理解上步骤
解释一下:
步骤一在干嘛:12345下一个是12354,为什么?变得是45交换,不是34交换。因为下一个排列比上一个排列的数值大,且仅大一点。所以我们去先修改个位。从右找到第一个递增相邻。
步骤二在干嘛:如13254的从右第一个递增相邻是25,所以2要换成其他值 ,那后面有54我们选4不选5为什么呢?因为放在2的位置>2,有点不好说,根据规律得出结论。
步骤三交换:13254变13452
步骤四:对于步骤三4位置之后因为刚交换过吗,所以后面应该是最小的排列方式即递增方式