问题描述
给定一个字符串
s
,检查该字符串是不是回文的排列,即该字符串能否构成回文。
解法
解法一
思路
如果这个字符串s
能构成回文,那么s
中出现的字符要么全部出现偶数次,要么有且只有一个字符出现奇数次。
时间复杂度: O(N),N是字符串的长度
步骤:
- 去除s中的非字母字符
- 检查s中出现奇数次字符的个数,小于等于1次,返回真,大于1次,返回假
代码
public class PalindromePermutation {
public boolean isPermutatuinOfPalindrome_CountOdd(String phrase) {
int[] table = new int[26]; // 26个字母
boolean foundOdd = false; // 是否有奇数个字符
for (char c : phrase.toCharArray()) {
int x = getCharNumber(c);
if (x != -1) {
table[x]++;
}
}
for (int count : table) {
if (count % 2 == 1) {
if (foundOdd) {
return false;
}
foundOdd = true;
}
}
return true;
}
/*
* 返回字符在字母表中的位置a->0,b->1... 非字母字符返回-1 大小写不敏感
*/
private int getCharNumber(char c) {
int a = Character.getNumericValue('a');
int z = Character.getNumericValue('z');
int val = Character.getNumericValue(c);
if (a <= val && val <= z) {
return val - a;
}
return -1;
}
}
解法二
思路
时间复杂度没法更优,因为必须将字符串完整遍历一遍。相较于解法一的遍历完之后再检查奇数次字符的个数,我们可以在遍历的过程中就检查奇数次字符的个数。遍历完字符串之后立刻就能获得结果。
代码
public boolean isPermutatuinOfPalindrome_CountOdd_OnGoing(String phrase) {
int countOdd = 0;
int[] table = new int[26];
for (char c : phrase.toCharArray()) {
int x = getCharNumber(c);
if (x != -1) {
table[x]++;
if (table[x] % 2 == 1)
countOdd++;
else
countOdd--;
}
}
return countOdd <= 1;
}
但是这个方法并不见得比方法一更快。此方法时间复杂度也是O(N)。虽然我们避免了对table
数组的遍历,因但是遍历字符串的过程中每个字符的操作更多。
解法三
思路
如果再深入思考一下,我们会发现其实并不用知道没个字符的出现的次数,只需要知道字符出现次数是偶数还是奇数就行了。比如说一个一开始是熄灭的灯,当我们开关它多次之后,如果最终仍是熄灭状态,虽然不知道开关了多少次,但是我们知道肯定是偶数次。
我们可以使用一个整数来作为一个二进制向量。对于字符串中的每一个字符,将其映射到这个向量上。映射过程就是将二进制向量的第i
位的值反转(i是这个字符在字母表中的位置)。遍历结束之后,检查二进制向量中是否最多只有一个1。
检查1的个数的方法:假如二进制向量是0001 0000
。我们可以挨个检查看是否只有一个1。而另一个较简介快速的方法是,用它减去1,得到0000 1111
。得出的这个结果和原数字没有重合(如果原数字是0010 1000
,减1的结果是0010 0111
,从左往右第三位有重合)。所以我们可以利用这种方式来检查二进制向量里1的个数。
时间复杂度同样是O(N)。
代码
public boolean isPermutatuinOfPalindrome_BitVector(String phrase) {
int bitVector = 0;
for (char c : phrase.toCharArray()) {
int x = getCharNumber(c);
bitVector = toggle(bitVector, x);
}
return bitVector == 0 || ((bitVector & (bitVector - 1)) == 0);
}
/* 反转二进制向量的第index位的值 */
private int toggle(int bitVector, int index) {
if (index < 0)
return bitVector;
int mask = 1 << index;
if ((bitVector & mask) == 0)
bitVector |= mask; // bitVector第index位由0反转至1
else
bitVector &= ~mask; // bitVector第index位由1反转至0
return bitVector;
}