1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Solution {
public List<String> generatePalindromes(String s) {
int[] charCount = new int[26];
int oddCount = 0;
for (char letter : s.toCharArray()) {
charCount[letter - 'a'] += 1;
if (charCount[letter - 'a'] % 2 == 1) {
oddCount += 1;
} else {
oddCount -= 1;
}
}
if (oddCount > 1) {
return new ArrayList<>();
}

String oddLetter = "";
List<Character> candidates = new ArrayList<>();
for (int i = 0; i < charCount.length; i++) {
char currLetter = (char) (i + 'a');
if (charCount[i] % 2 != 0) {
oddLetter = String.valueOf(currLetter);
charCount[i] -= 1;
}
for (int j = 0; j < charCount[i] / 2; j++) {
candidates.add(currLetter);
}
}
List<String> ans = new ArrayList<>();
dfs(ans, candidates, new StringBuilder(), new boolean[candidates.size()], oddLetter);
return ans;
}

private void dfs(List<String> ans, List<Character> candidates, StringBuilder curr, boolean[] used, String mid) {
if (curr.length() == candidates.size()) {
ans.add(curr.toString() + mid + curr.reverse().toString());
curr.reverse();
return;
}
for (int i = 0; i < candidates.size(); i++) {
if (used[i] || (i > 0 && candidates.get(i) == candidates.get(i - 1) && !used[i - 1])) {
continue;
}
curr.append(candidates.get(i));
used[i] = true;
dfs(ans, candidates, curr, used, mid);
used[i] = false;
curr.setLength(curr.length() - 1);
}
}
}

先统计每个字母出现的频率, 看出现奇数次letter的个数是否大于1, 如果是, 那就组不成palindrome, 不是的话继续下面的操作. 然后我们统计palindrome一侧可以出现哪些字母. 这里需要注意的是, 出现奇数次的letter(如果有的话)虽然中间是它, 但是它也是可以出现在一侧的, 并且有可能出现多次, 我们有时候会默认出现奇数次的letter只在中间出现, 这是不对的, 这样默认它只出现一次, 但是实际上可能是多次. 然后我们开始用backtrack. 在某个位置, 我们从到到尾遍历, 挑一个字符出现在当前的位置, 之前选中的无法再选, 还有就是如果之前某个字母在在这一层被选择了, 它就不能再次被选. 第一种情况好说, 用一个boolean数组, 记录每个位置的字符是否已经被选中放进了StringBuilder中. 第二种情况如何判断? 由于我们是从0到尾挨个让没有被选中的字母坐当前的位置, 于是如果某个字母之前被选择过在这里, 并且它在candidates中也是出现多次, 由于我们放letter进candidates中时, 相同letter都是挨着的, 于是当我们发现当前字母和之前一个字母一样, 并且之前字母没有被选中, 这就说明上一个字母坐过当前的位置(没有被选中说明上一个字母是可以在这一层被选中, 并且它有出现在前一个位置, 因此它在当前层之前被选中过), 因此我们就跳过此时的字母, 避免重复计算. 这个方法很妙.

时间复杂度和空间复杂度不好求.