266 |
Palindrome Permutation | 52.6% | Easy | |||
46 | Permutations | 37.7% | Medium | |||
267 | Palindrome Permutation II | 29.5% | Medium | |||
47 | Permutations II | 29.3% | Medium | |||
31 | Next Permutation | 27.3% | Medium | |||
60 | Permutation Sequence | 26.0% | Medium |
第一 如何求一个字符串的全排列
求全排列比较常见的是两种方法,一种是递归,一种是字典序的方法,递归思想是这样的,我们试想一个简单的例子,123的全排列:
123
132
213
231
312
321
我们可以理解为分别将长度为n的数组的每一个数与数组的第一个数进行交换,然后对后面的n-1子数组进行全排列,这样得到的代码也比较简洁:
public void arrange (String[] str, int st, int len) { if (st == len - 1) { for (int i = 0; i < len; i ++) { System.out.print(str[i]+ " "); } System.out.println(); } else { for (int i = st; i < len; i ++) { swap(str, st, i); arrange(str, st + 1, len); swap(str, st, i); } } }字典序是指将一个字符串以字典序升序的方式排列,再如上例的123,当按照一定的交换规则变成321,即不存在升序的时候表示排列完成,字典序的全排列生成是有严格顺序的,c++中有next_permutation函数来生成一个排列的下一个字典序排列,字典序的生成规则如下:
一般而言,设P是[1,n]的一个全排列。 P=P1P2…Pn=P1P2…Pj-1PjPj+1…Pk-1PkPk+1…Pn find: j=max{i|Pi1}
k=max{i|Pi>Pj} 1, 对换Pj,Pk,
2, 将Pj+1…Pk-1PjPk+1…Pn翻转
P’= P1P2…Pj-1PkPn…Pk+1PjPk-1…Pj+1即P的下一个
【例】 如何得到346987521的下一个 1,从尾部往前找第一个P(i-1) < P(i)的位置 3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1 最终找到6是第一个变小的数字,记录下6的位置i-1 2,从i位置往后找到最后一个大于6的数 3 4 6 -> 9 -> 8 -> 7 5 2 1 最终找到7的位置,记录位置为m 3,交换位置i-1和m的值 3 4 7 9 8 6 5 2 1 4,倒序i位置后的所有数据 3 4 7 1 2 5 6 8 9 则347125689为346987521的下一个排列next_permutation java代码:
public void nextPermutation(int[] num) { //1.找到最后一个升序位置pos int pos = -1; for (int i = num.length - 1; i > 0; i--) { if (num[i] > num[i - 1]) { pos = i - 1; break; } } //2.如果不存在升序,即这个数是最大的,那么反排这个数组 if (pos < 0) { reverse(num, 0, num.length - 1); return; } //3.存在升序,那么找到pos之后最后一个比它大的位置 for (int i = num.length - 1; i > pos; i--) { if (num[i] > num[pos]) { int tmp = num[i]; num[i] = num[pos]; num[pos] = tmp; break; } } //4.反排pos之后的数 reverse(num, pos + 1, num.length - 1); } public void reverse(int[] num, int begin, int end) { int l = begin, r = end; while (l < r) { int tmp = num[l]; num[l] = num[r]; num[r] = tmp; l++; r--; } }代码是leetcode next permutation的ac代码,按照要求当字符串是完全降序的时候倒序字符串,整个过程就是next_permutation的思想。
下面来看一下palindrome permutationII和permutation sequence两道题:
Given a string s
, return all the palindromic permutations (without duplicates) of it. Return an empty list if no palindromic permutation could be form.
For example:
Given s = "aabb"
, return ["abba", "baab"]
.
Given s = "abc"
, return []
.
Hint:
第一,生成回文全排列,我们只需要生成前半部分,然后倒转生成后半部分即可;
第二,生成全排列的方法,这个生成方法有很多,记得需要考虑重复元素的问题;
需要注意的地方有:
1)回文只允许至多一个奇数次的字符,若多于一个必定无法构成回文。
2)奇数长度和偶数长度如何处理
这里引用https://discuss.leetcode.com/topic/22214/ac-java-solution-with-explanation的代码,清晰明了:
public ListgeneratePalindromes(String s) { int odd=0; List list=new ArrayList<>(); Map ,Integer> map=new HashMap<>(); for(int i=0;i ;i++){ char temp=s.charAt(i); map.put(temp,map.containsKey(temp)?map.get(temp)+1:1); odd+=map.get(temp)%2==1?1:-1; } String str=""; List charlist=new ArrayList<>(); if(odd>1) return list; for(Character key:map.keySet()){ if(map.get(key)%2==1) str+=key; int val=map.get(key); for(int i=0;i 2;i++){ charlist.add(key); } } getPumu(list,charlist,new StringBuilder(),new boolean[charlist.size()],str); return list; } public void getPumu(List list,List charlist,StringBuilder sb,boolean[] used,String str){ if(sb.length()==charlist.size()){ list.add(sb.toString()+str+sb.reverse().toString()); sb.reverse(); return; } for(int i=0;i ;i++){ if(i>0&&charlist.get(i)==charlist.get(i-1)&&!used[i-1]) continue; if(!used[i]){ used[i]=true; sb.append(charlist.get(i)); getPumu(list,charlist,sb,used,str); used[i]=false; sb.deleteCharAt(sb.length()-1); } } }
The set [1,2,3,…,n]
contains a total of n! unique permutations.
By listing and labeling all of the permutations in order,
We get the following sequence (ie, for n = 3):
"123"
"132"
"213"
"231"
"312"
"321"
Given n and k, return the kth permutation sequence.
Note: Given n will be between 1 and 9 inclusive.
当然可以用next_pumutaion多次调用,但是细心观察显然是有数学规律的,显然1-n的全排列有n!种排列,而以1、2、3......n为开头的排列各有(n-1)!种排列,那么所求p=k/(n-1)!的nums[p]即为该排列的第一个数字,依次类推可以求出该排列的所有数字,代码:
public String getPermutation(int n, int k) { List list=new ArrayList<>(); int multiply=1; for(int i=0;i;i++){ list.add(i+1); multiply*=i+1; } String res=""; for(int i=0;i ;i++){ multiply=multiply/(n-i); int index=multiply/k; res+=list.get(index); list.remove(index); k=multiply%k; } return res; }