5. 最长回文子串

给定一个字符串 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:

定义两个变量startmaxLen,分别表示最长回文子串的起点跟长度,在遍历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);
    }
};

我们用两个变量leftright分别指向当前位置,然后向右遍历跳过重复项,这个操作很必要,比如对于 nooni在第一个o的位置,如果我们以o为最中心往两边扩散,是无法得到长度为4的回文串的,只有先跳过重复,此时left指向第一个oright指向第二个o,然后再向两边扩散。而对于 bobi在第一个o的位置时,无法向右跳过重复,此时leftright同时指向o,再向两边扩散也是正确的,所以可以同时处理奇数和偶数的回文串,之后的操作就是更新maxLenstart

遇到的几个错误:

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;

 

还有一种最简单的方法:马拉车算法,等日后再做吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值