Leetcode刷题笔记 Day3 (热题100 using C++)

T5 最长回文子串

力扣icon-default.png?t=M666https://leetcode.cn/problems/longest-palindromic-substring/法一:中心扩展

基本思想:遍历字符串中的每个字符将其作为中心,向两边进行扩展,直至到达字符串头尾或不为回文子串,再计算长度即可,保留最长的范围。

class Solution {
public:
    pair<int, int> expand(string &s, int left, int right){
        while(left>=0 && right<s.length() && s[left]==s[right]){
            left--;
            right++;
        }
        return make_pair(left+1, right-1);  //注意此处需要向中心聚拢一,因为当前left和right区间内不满足条件
    }
    string longestPalindrome(string s) {
        int n=s.length();
        int start=0, end=0;
        for(int i=0; i<n; i++){
            auto [a1,b1]=expand(s,i,i);
            auto [a2,b2]=expand(s,i,i+1);
            if(b1-a1>end-start){
                start=a1;
                end=b1;
            }
            if(b2-a2>end-start){
                start=a2;
                end=b2;
            }
        }
        return s.substr(start,end-start+1);
    }
};

个人感觉这种算法理解起来十分简单,但是感觉还是没有充分利用到前序已经求解的结果。

法二:Manacher 算法

核心思想:利用前序信息。假定回文子串长度为奇数,引入臂长的概念,中心为s[j],回文子串为s[j-length]到s[j+length],总长度为2*length+1,臂长为length。

若已知s[j]的臂长为length,且目前遍历的中心i满足i<j+length,即在以j为中心的回文子串中,则可利用前序信息:

① 首先查找i关于j的对称点2*j-i,若该点的臂长为n,则i的臂长至少为min(j+length-i, n);

解释:n的情况:以对称点为中心的回文子串完全包含在以j为中心的回文子串中;

           j+length-i的情况:以对称点为中心的回文子串完全未包含在以j为中心的回文子串中;

② 继续从i+min(j+length-i, n)+1向外拓展。

还需要考虑长度为偶数的回文子串,在字符串头尾和每两个字符串中间插入一个特殊字符如#(需要注意不能为字符串中原有的字符),原来的偶数长度回文串就可以转换为奇数长度回文串,如aa变为#a#a#

实际实现中,直接将原字符串添加特殊字符串在进行处理,维护一个当前到达的最右回文子串。

class Solution {
public:
    int expand(string &s, int left, int right){     //  用于计算臂长
        while(left>=0 && right<s.length() && s[left]==s[right]){
            left--;
            right++;
        }
        return (right-left-2)/2;
        //与前一种算法一样,注意此处需要向中心聚拢一,因为当前left和right区间内不满足条件
    }
    string longestPalindrome(string s) {
        int start=0,end=0;
        string t="#";
        for(int i=0;i<s.length();i++){
            t+=s[i];
            t+='#';
        }
        s=t;

        vector<int> arm;
        int right=-1,j=-1;  //j为最长回文子串的中心
        int n=s.length();
        for(int i=0;i<n;i++){
            int cur;
            if(right>=i){
                int sym=2*j-i;   //对称点
                int min_arm=min(arm[sym],right-i);
                cur=expand(s,i-min_arm,i+min_arm);
            }
            else{
                cur=expand(s,i,i);
            }
            arm.push_back(cur);
            //下面对最长回文子串和最右回文子串进行更新
            if(i+cur>right){
                j=i;
                right=i+cur;
            }
            if(cur*2+1>end-start+1){
                start=i-cur;
                end=i+cur;
            }
        }
        string ans;
        for(int i=start;i<=end;i++){
            if(s[i]!='#'){
                ans+=s[i];
            }    
        }
        return ans;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值