给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb"
思考:
传统的验证回文串的方法就是两个两个的对称验证是否相等,那么对于找回文字串的问题,就要以每一个字符为中心,像两边扩散来寻找回文串。这个算法的时间复杂度是O(n*n),要注意奇偶情况需要分开讨论。
解法1:
void searchPalindrome(int left,int right,int &start,int &max_len,string s){
while(left >= 0 && right <s.size() && s[left] == s[right]){
left--;
right++;
}
//if(max_len < right - left +1-2) right 和 left 在判断最后一次相等之后,分别还向远处移动了1位,所以应该减去2,
//right-left+1 表示下表为left 和 right 之间的元素个数
if(max_len < right - left -1){
max_len = right - left -1;
start = left +1;
}
}
class Solution {
public:
string longestPalindrome(string s) {
if(s.size()<2) return s;
int left =0,right =0,start = 0,max_len=0;
//分别进行最长回文子串为奇数和偶数的情况查询
for(int i = 0 ;i < s.size();i++){
//若是奇数只需要从当前位置开始查询
searchPalindrome(i,i,start,max_len,s);
//若是偶数需要从当前位置和下一个位置开始像两侧查询
searchPalindrome(i,i+1,start,max_len,s);
}
return s.substr(start,max_len);
}
};
解法2:
定义两个变量start和maxLen,分别表示最长回文子串的起点跟长度,在遍历s中的字符的时候,我们首先判断剩余的字符数是否小于等于maxLen的一半,是的话说明maxLen无法再变长了,直接break掉。(因为是从某个字符往两边扩充,当剩余的小于或者等于maxlen的一半时,即使剩下的全部都可以构成,那么最长也是小于maxlen的,所以可以跳出循环了)
class Solution {
public:
string longestPalindrome(string s) {
if(s.size() < 2) return s;
int maxlen = 0,start = 0;
int n = s.size();
for(int i =0;i<n;){
if(n - i <= maxlen /2) break;
int left = i,right = i;
//向右跨过相同的元素
while(right < n-1 && s[right+1] == s[right]) right++;
//此时若在想检查,应该从当前right元素的下一位开始
i = right+1;
while(left > 0 && right <n-1 && s[right+1]== s[left-1]){
left--;
right++;
}
//if(maxlen < right -left -1){
if(maxlen < right -left +1){
maxlen = right -left +1;
// start = left+1;
start = left;
}
}
return s.substr(start,maxlen);
}
};
我们用两个变量left和right分别指向当前位置,然后向右遍历跳过重复项,这个操作很必要,比如对于 noon,i在第一个o的位置,如果我们以o为最中心往两边扩散,是无法得到长度为4的回文串的,只有先跳过重复,此时left指向第一个o,right指向第二个o,然后再向两边扩散。而对于 bob,i在第一个o的位置时,无法向右跳过重复,此时left和right同时指向o,再向两边扩散也是正确的,所以可以同时处理奇数和偶数的回文串,之后的操作就是更新maxLen和start了
遇到的几个错误:
1、这里和上面一种解法不同的地方在于,比较了当前两项不相等,就是向右遍历跳过重复项,所以在进行循环的时候直接去比较left和right更远的一项就可以了。
while(left > 0 && right <n-1 && s[right+1]== s[left-1])
2、写成这种形式还是以为循环完成之后left和right都向前夺走了一步,实际上这里比较的就是s[right+1]== s[left-1]),即当前位置的远处一位,所以left和right是当前位置,直接+1就可以了。
//if(maxlen < right -left -1)
if(maxlen < right -left +1)
3、这个错误还是没有弄清left的位置
// start = left+1;
start = left;
还有一种最简单的方法:马拉车算法,等日后再做吧。