这一题最主要的就是permutation中可自由permute的部分,对于Palindrome而言,前半部分的内容就已经决定了后半部分的内容,所以实质可以自由变换的部分就是前半部分。而对于palindrome而言,前半部分的内容,恰好就是由整体input的字符各取一半组成的。所以基本上,就是从整个input里,取一半的字符(如果字符个数是奇数且能够成为palindrome,多出来的那个非偶数的字符必然就是呆在中间的那个字符),然后排序也好随便也好组成一个新的字符串,并且对这个新的字符串进行permutation,每当一个permutation的结果出来,做镜像得到后半部分的字符串并且组合即可。
这一题的原理不是很复杂,代码就有点长了。因为主要由四步组成
1. 判断输入是否合法(也就是Palindrome Permutation)
2. 构成由半数字符的新字符串
3. permute这个新的字符串
4. 根据permute的结果进行镜像化处理得到最后的结果。
private boolean checkPalinAndReorderOdd(char[] arr) {
Map<Character, Integer> charCnt = new HashMap<>();
int oddPos = -1;
for (char ch : arr) {
charCnt.put(ch, charCnt.getOrDefault(ch, 0) + 1);
}
int indx = 0;
boolean oddMet = false;
for (char c : charCnt.keySet()) {
int cnt = charCnt.get(c);
for (int i = 0; i < cnt / 2; i++) {
arr[indx] = c;
indx++;
}
if (cnt % 2 != 0) {
if (!oddMet && arr.length % 2 == 1) {
oddMet = true;
arr[arr.length / 2] = c;
} else {
return false;
}
}
}
return true;
}
public List<String> generatePalindromes(String s) {
char[] chArr = s.toCharArray();
List<String> result = new LinkedList<>();
if (!checkPalinAndReorderOdd(chArr)) return result;
permutePalindrome(chArr, 0, result);
return result;
}
private void permutePalindrome(char[] arr, int pos, List<String> result) {
if (pos >= arr.length / 2) {
for (int i = 0; i < arr.length / 2; i++) {
arr[arr.length - 1 - i] = arr[i];
}
result.add(new String(arr));
} else {
Set<Character> visited = new HashSet<>();
for (int i = pos; i < arr.length / 2; i++) {
if (visited.contains(arr[i])) continue;
swap(arr, i, pos);
permutePalindrome(arr, pos + 1, result);
swap(arr, i, pos);
visited.add(arr[i]);
}
}
}
private void swap(char[] arr, int pos1, int pos2) {
char tmp = arr[pos1];
arr[pos1] = arr[pos2];
arr[pos2] = tmp;
}
为了假装自己是inplace做的permutation(基于swap),这里的冗余代码真的太多了。自己又写了一个新的版本,performance更好,更简洁一些,相比用重组的字符串直接通过swap做permutation,用更原始的办法,即是一个字符统计器做permutation。代码如下:
public HashMap<Character, Integer> checkPDAndReorderArr(char[] chArr) {
HashMap<Character, Integer> chCnt = new HashMap<>();
for (char c : chArr) chCnt.put(c, chCnt.getOrDefault(c, 0) + 1);
boolean oddMet = false;
for (Character c : chCnt.keySet()) {
if (chCnt.get(c) % 2 == 1 && oddMet) return null;
else if (chCnt.get(c) % 2 == 1) {
oddMet = true;
chArr[chArr.length / 2] = c;
chCnt.put(c, chCnt.get(c) - 1);
}
}
return chCnt;
}
public List<String> generatePalindromes(String s) {
char[] chArr = s.toCharArray();
List<String> result = new LinkedList<>();
HashMap<Character, Integer> chCnt = checkPDAndReorderArr(chArr);
if (chCnt == null) return result;
genPDFromCounts(chCnt, chArr, result, 0);
return result;
}
public void genPDFromCounts(HashMap<Character,Integer> chCnt, char[] cache, List<String> result, int cursor) {
if (cursor >= cache.length / 2) {
result.add(new String(cache));
} else {
for (Character c : chCnt.keySet()) {
if (chCnt.get(c) == 0) continue;
cache[cursor] = cache[cache.length - 1 - cursor] = c;
chCnt.put(c, chCnt.get(c) - 2);
genPDFromCounts(chCnt, cache, result, cursor + 1);
chCnt.put(c, chCnt.get(c) + 2);
}
}
}