DESC1:
无重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合,字符串每个字符均不相同。
示例1:
输入:S = "qwe"
输出:["qwe", "qew", "wqe", "weq", "ewq", "eqw"]示例2:
输入:S = "ab"
输出:["ab", "ba"]提示:
字符都是英文字母。
字符串长度在[1, 9]之间。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-i-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
CODE:
JAVA:
class Solution { public String[] permutation(String S) { if (S == null || S.length() == 0) { return new String[0]; } char[] arr = S.toCharArray(); boolean[] flag = new boolean[arr.length]; List<String> res = new ArrayList<>(); StringBuilder sb = new StringBuilder(); backTrack(res, arr, flag, sb); String[] temp = new String[res.size()]; for (int i=0; i<res.size(); i++) { temp[i] = res.get(i); } return temp; } public void backTrack(List<String> res, char[] arr, boolean[] flag, StringBuilder sb) { if (sb.length() == arr.length) { res.add(sb.toString()); return; } for (int i=0; i<arr.length; i++) { //已走过,跳过 q-[q(跳过),w,e] if (flag[i]) { continue; } sb.append(arr[i]); flag[i] = true; backTrack(res, arr, flag, sb);//如q-w这一条路径方向 flag[i] = false;//q-w走完,下一次该走q-e,所以需要将flag中w恢复,sb也要移除w sb.deleteCharAt(sb.length()-1);//注意deleteCharAt,非remove } } }
NOTES:
- 回溯思想,递归
- 第一队列遍历每个字符,然后以每个字符为起始点,第二队列,再次递归每个字符进行追加,遇到第一队列已走过的点跳过,然后再进入第三队列迭代递归,所以需要一个状态数组来记录追加路径中已走过的点
- 重要的是,在同一队列中,某节点走完其路径,要注意回溯到队列之前状态,在进行下一节点递归,如第一队列为q点,进入第二队列,q,w,e三个节点,q在第一队列已走过直接跳过,走w节点,此时q-w路径开始递归进入第三队列获取所有队列,结束后,继续第二队列,此时需要将w节点的状态置为false,路径中移除,恢复到第二队列的进入状态,才可进行下一节点q-e的迭代。也就是所谓的回溯思想~
- 结束条件,就是路径的长度等于字符串的长度,说明都走过了;
进阶,假如字符串并不是非重复的,可能包含重复字符,怎么解~
DESC2:
有重复字符串的排列组合。编写一种方法,计算某字符串的所有排列组合。
示例1:
输入:S = "qqe"
输出:["eqq","qeq","qqe"]示例2:
输入:S = "ab"
输出:["ab", "ba"]提示:
字符都是英文字母。
字符串长度在[1, 9]之间。来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-ii-lcci
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
CODE:
JAVA:
class Solution { public String[] permutation(String S) { if (S == null || S.length()==0) { return new String[0]; } char[] arr = S.toCharArray(); //排序,是重复字符挨在一起 Arrays.sort(arr); boolean[] flag = new boolean[arr.length]; List<String> resList = new ArrayList<>(); StringBuilder sb = new StringBuilder(); backTrack(resList, arr, flag, sb); String[] res = new String[resList.size()]; for (int i=0; i<resList.size(); i++) { res[i] = resList.get(i); } return res; } public void backTrack(List<String> resList, char[] arr, boolean[] flag, StringBuilder sb) { if (sb.length() == arr.length) { resList.add(sb.toString()); return; } for (int i=0; i<arr.length; i++) { //如果再同一队列中,如abbbc,两个bb产生的路径是一样的,所以后面的跳过 if (flag[i] || (i>0 && arr[i] == arr[i-1] && !flag[i-1])) { continue; } sb.append(arr[i]); flag[i] = true; backTrack(resList, arr, flag, sb); flag[i] = false; sb.deleteCharAt(sb.length()-1); } } }
NOTES:
- 有重复字符,如果还是和题一一样,肯定会有重复序列,所以需要去重,首先对字符序列排序,使重复字符挨着;
- 重复序列产生的时机是在同一队列遍历时,如bbbc,在第一队列[b,b,b,c]中,三个以b开头的节点产生的路径肯定是一样的,所以只走一个就好。即当满足(i>0 && arr[i] == arr[i-1])条件时,说明碰上了重复字符,则跳过;这样是否就可以了呢,仔细想想或者画一画你会发现在第二队列,正常的路径也被跳过了,所以需要加上 && !flag[i-1]条件,即需满足不是在跨队列正常的路径中。如bbbc这个b-b(1)-跳过,是因为第一队列已走过, 但b-b(2)-不能被过滤,1,2代表第二队列第一个字符,b-b(3)-重复过滤;