题目:
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例 1:
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
思路
-
很容易想到做一个int[26+26]数组来保存各字母的个数,但大写A~Z和小写a~z的ASCII码是不连续的,可参考ASCII表。
-
如果某字母有偶数个,因为偶数有对称性,可以把它全部用来构造回文串;但如果是奇数个的话,并不是完全不可以用来构建,也不是只能选最长的那个,而是只要砍掉一个,剩下的变成偶数就可以全部计入
-
但奇数字母里,可以保留一个不砍,把它作为回文串的中心,所以最后加一。
代码
public static int longestPalindrome(String s) {
if (s == null || s.isEmpty()) return 0; //判断空值
int[] countArray = new int[58];//大写字母26个+小写字母26个,大写和小写的ASCII码之间存在6个特殊字符,所以length = 58
for (char c : s.toCharArray()) {
countArray[c - 65] += 1;//大写的A最小等于65,将所有字符出现的此时存储在对于的位置
}
int number = 0;
for (int x: countArray) {
if (x % 2 == 0){//如果字符出现次数是偶数,则所有的都将参与到回文字符串中
number += x;
}else{
number += x - 1;//如果是奇数,则最多可以有奇数减一个参与回文字符串中
}
}
//由于在上面的奇数处理中所有的奇数都减一了,如果number小于s的length则说明存在奇数的字符,
//在回文字符串中我们可以将一个出现奇数次的字符放置在回文字符串的中间,所以number小于length则需要加一
//不小于说明等于,则没有奇数次的字符。则所有字符都参与回文字符串拼接中。
return number < s.length() ? number + 1 : number;
}
代码优化(参考网友的)
public int longestPalindrome(String s) {
int[] cnt = new int[58];
for (char c : s.toCharArray()) {
cnt[c - 'A'] += 1;
}
int ans = 0;
for (int x: cnt) {
ans += x - (x & 1);//这一步很巧妙,可以学习下。
}
return ans < s.length() ? ans + 1 : ans;
}