题目
给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例 1:
输入:
"abccccdd"
输出:
7
解释:
我们可以构造的最长的回文串是"dccaccd", 它的长度是 7。
解题思路
首先,回文串基本有以下两种形式:
ABA(两边出现的字符串成对出现,并可存在也仅能存在一个中间字符)
AA(只有两边成对出现的字符)
可以看到回文串形式都需要两边出现成对的字符,因此,在本题中,应尽可能把所有偶数次数的字符取出来(例如,有3个相同的字符则取完偶数2个字符),然后再根据由所有偶数次字符组成的回文串长度来判断是否能组成:ABA,这种含有中间字符形式的回文串,即可得到在最后答案。
实现思路
- 把输入字符串所有字符计次保存进hash表。
- 遍历整个hash表存储的所有值,把偶数部分取出来,作为累加长度
- 遍历完hash表后,判断当前累加的长度是否等于原字符串长度,如果小于原字符串长度,则证明还存在奇数次的字符未计入回文串中,这时候则可随意取任意一个奇数字符组成:ABA,这种存在中间字符的回文串,即最长回文串长度+1,即可得到最后结果。
小技巧
- 小写字母’z’跟大写字母起始’A’之间的Ascii码相差57(具体忘记的可直接用下面提供的代码输出验证),因此,所有小写、大写英文字母最多占用58个位置,所以,我们可以声明一个58大小的Int型数组作为一个计算字母出现次数的hash表使用。
- 用位移运算符,先右移(>>)再左移(<<)结果等同于:(x / 2) * 2,可直接把数值中偶数部分取出来(例如:1001取出1000,9取出8)。这里看到有高手用更巧妙的方法,设数值为x:x - (x&1),即可得出偶数部分。
验证代码
func main() {
fmt.Println('z' - 'A')
}
输入结果
57
解题代码
func longestPalindrome(s string) int {
byteCount := make([]int, 58)
for _, v := range s {
byteCount[v - 'A']++
}
maxLen := 0
for _, v := range byteCount {
maxLen += (v >> 1) << 1 //或者写成v - (v & 1)这种效率更高的写法
}
if maxLen < len(s) {
maxLen++
}
return maxLen
}