【leetcode】最长回文子串

给你一个字符串 s,找到 s 中最长的回文子串。
示例 1:
输入:s = “babad”
输出:“bab”
解释:“aba” 同样是符合题意的答案。
示例 2:
输入:s = “cbbd”
输出:“bb”

这有个妙法:马拉车(manancher)

不过,不过,马拉车算法几乎不太可能出现在一般面试当中,是属于程序竞赛范畴的解法。
》》改进一
中心探测法一个不好之处,在于它要分奇偶(中心是两个相同字符,还是一个字符)
我们怎么统一它呢?加入分隔符
比如“abccba”,变成“#a#b#c#c#b#a#”
就可以忽略奇偶长度。
在这里插入图片描述
我们可以找到回文半径和最长回文子串长度之间的数量关系。
比如上图我们称之为 s s s的分隔处理之后的字符串, s [ 9 ] s[9] s[9]的回文半径长度为7,而对应的回文串’#a#b#b#c#b#b#a#'长度为15,也即7*2-1,我们去掉分隔符的原回文子串长度也是这样计算的。故回文半径长度等于原串回文字串长度。

//i:当前遍历到的数组下标
//armLen:以i为中心,计算出的回文半径
start = (i - armLen) / 2;
maxLen = armLen;

之后根据start和maxLen,就能截取最长回文字串咯~
》》改进二
这样还不够,我们可以看出,在计算到c的时候,从s[9](center)到s[16](right)都是和左边对称的。那么左边求过的小值还可以再用。
这里又要分情况了。
1.right < i:
在这里插入图片描述
也就是当迭代器走到红色箭头及以外部分,就没有左边对称的值可以白嫖了;只好用中心探测法,一点一点的向外扩展计算。
2.right>=i
2.1 如果right - i > p[i_mirror]
在这里插入图片描述
上图中,p[i_mirror]为2,right-i为4.那么就放心,向右扩展是扩不到right之外的。但是,剩下的两位呢?我们还要计算出两个新起点i_left和i_right,然后从这两个起点向左右延申。这等于是跳过了一些检查的位置,这就是马拉车算法的关键所在,利用已知的i_mirror信息来扩展i,跳过一些已经确定的位置,省去了多余的判断。(如下图)在这里插入图片描述
2.2 如果right-i<p[i_mirror]
这是什么意思呢?也就是说我们可探测到的最右端仅仅是right,right往右的值不知道。所以这时候这个p[i]不能是p[i_mirror],而只能是right-i了。
综合2.1,2.2,得出如果(right-i)!=p[i_mirror],需取二者中较小的值送给p[i],然后从right再扩展。
2.3 如果right - i == p[i_mirror]
在这里插入图片描述
如上图,p[i_mirror]为1,right - i也是为1。
这就说明以i为中心至少可以再向左右扩展1位。
我们让i_left = i - 1,让i_right = i + 1,然后以i_left和i_right为新起点,继续往左/右扩展。

综上,计算的伪代码:

i_mirror = 2 * center - i;
最小扩展单位 = min(right - i, p[i_mirror]);
以i为中心的回文半径 = expand(i - 最小扩展单位, i + 最小扩展单位)

AC代码:(时间100%,空间70%)

class Solution {
public:
    //马拉车算法
    string longestPalindrome(string s)
    {
    	//插入"#"
        string t="$#";
        for(auto c:s){
            t+=c;
            t+='#';
        }
        // cout << t << endl;
        vector<int> p(t.size(),0);
        
        //mx表示某个回文串延伸在最右端半径的下标,id表示这个回文子串最中间位置下标
    	//resLen表示对应在s中的最大子回文串的半径,resCenter表示最大子回文串的中间位置
        int mx=0,id=0,resLen=0,resCenter=0;
        
        //建立p数组
        for(int i=1;i<t.size();++i)
        {
        	//更新p[i]的值
            p[i]=mx>i?min(p[2*id-i],mx-i):1;
            
            //遇到三种特殊的情况,需要利用中心扩展法
            while(t[i+p[i]]==t[i-p[i]])p[i]++;
            
            //半径下标i+p[i]超过边界mx,需要更新
            if(mx<i+p[i]){
                mx=i+p[i];
                id=i;
            }
            
            //更新最大回文子串的信息
            if(resLen<p[i]){
                resLen=p[i];
                resCenter=i;
            }
        }
        
        //最长回文子串长度为半径-1,起始位置为中间位置减去半径再除以2
        return s.substr((resCenter-resLen)/2,resLen-1);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值