目录
- 题目
- 思路
- 优化
题目
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例1:
输入: "babad"
输出: "bab"
注意: "aba"也是一个有效答案。
实例2:
输入: "cbbd"
输出: "bb"
思路
这道题最暴力的就是直接判断所有子串是否是回文串,然后选择最长长度的回文串作为结果输出。然而这样一点都不优雅,稍微思考一下,觉得可以从每个位置出发,依次向两边遍历,比较两边的字符是否一致,按这种方法来选择回文串。提交了一次后发现过不了,像“abbc”这种串,第一个字符b的两侧分别为a和c,按照前面的方法比较不符合回文性质;同理第二个字符b也一样。所以就发现一个问题,对于相邻且相同的字符应该特殊处理一下,可以将它们作为一个整体,每次比较的是这个整体两侧的字符。于是有以下代码:
class Solution {
public:
string longestPalindrome(string s) {
string res = "";
for(int i = 0; i < s.length(); ){
int len = 1, start=i-1,end=i+1;
while(start>=0 && s[start] == s[i]) {
start--;
len++;
}
while(end<=(s.length()-1) && s[end] == s[i]){
end++;
len++;
}
while(start>=0 && end<=(s.length()-1)){
if(s[start]==s[end]){
len+=2;
start--;end++;
} else{
break;
}
}
if(len>res.length()){
res = s.substr(start+1, end-start-1);
cout << res << "\n";
}
i = temp;
}
return res;
}
};
做是做出来了,就是时间太慢了。不妥,继续优化。

优化
由于前面已经将相邻的相同字符作为一个整体处理,那么对于这些字符,只需执行一次查找即可。在代码中添加两处改变:
class Solution {
public:
string longestPalindrome(string s) {
string res = "";
for(int i = 0; i < s.length(); ){ // 不需要在for循环中执行 ++i 了
int len = 1, start=i-1,end=i+1;
while(start>=0 && s[start] == s[i]) {
start--;
len++;
}
while(end<=(s.length()-1) && s[end] == s[i]){
end++;
len++;
}
int temp = end; // 保存最后一个相同字符的后一个位置,作为i的下一个值
while(start>=0 && end<=(s.length()-1)){
if(s[start]==s[end]){
len+=2;
start--;end++;
} else{
break;
}
}
if(len>res.length()){
res = s.substr(start+1, end-start-1);
cout << res << "\n";
}
i = temp;
}
return res;
}
};
效果显著,从36ms到8ms。
仔细再想想,既然已经作为一个整体了,在第一次遇到一个字符的时候会自动将后面紧接着的相同字符也加进入作为一个整体,那么根本不要往前面找相同字符了。也就是说,下面代码是多余的,注释掉即可。
// while(start>=0 && s[start] == s[i]) {
// start--;
// len++;
// }
最后的结果,4ms,心满意足。