牛客网:最长回文子串

这里有两种方法解决

1.中心扩散

顾名思义,中心扩散就是以字符串中的某个字符为中心进行扩散,依次比较两边的字符是否相等,然后统计出最大长度,代码如下所示:

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param A string字符串 
     * @return int整型
     */
    int getLen(string &s, int begin, int end){
        while(begin>=0 && end<s.size() && s[begin]==s[end]){
            begin--;
            end++;
        }
        return end - begin - 1;
    }
    int getLongestPalindrome(string A) {
        int maxLen=1;
        for(int i=0;i<A.size();++i){
            maxLen=max(maxLen,  max(getLen(A, i, i), getLen(A, i, i + 1)));    
        }
        return maxLen;
    }
};

时间复杂度是O(n^2)。

编程时要注意回文字符串的长度可以是偶数长度或是奇数长度,这是个容易错的细节。

2.动态规划

这里有两种方法可以dp:

1.设定区间dp

设dp[l][r]表示在区间[l, r]内构成的字符串是否是回文子串。如果是则dp[i][j]=true,否则为false。然后我们可以写出如下转移方程:

dp[l][r]=\left\{\begin{matrix} true,l=r\\ s[l]==s[r],l+1=r\\ s[l]==s[r] \wedge dp[l+1][r-1] \end{matrix}\right.

翻译过来就是,只有一个字符的时候单个字符构成回文字符串,如果是两个字符,且两个字符相等,那么构成回文字符串,如果是其他情况,则在判断两个字符相等的条件下,再判断区间中的字符串是不是回文子串。

通过分析的过程我们在编程的时候应该从长度开始遍历,每个长度分别确定不同的左区间去看,分析完之后如果区间构成回文子串则判断长度是否更大。

代码如下所示:

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param A string字符串 
     * @return int整型
     */
    int getLongestPalindrome(string A) {
        int n = A.length();
        bool dp[n][n];
        int res=0;
        for(int len=0;len<n;++len){
            for(int l=0;l+len<n;++l){
                int r=l+len;
                if(len==0){
                    dp[l][r]=true;
                }else if(len==1){
                    dp[l][r]=(A[l]==A[r]);
                }else{
                    dp[l][r]=(A[l]==A[r] && dp[l+1][r-1]);
                }
                if(dp[l][r]){
                    res=max(res, r-l+1);
                }
            }
        }
        return res;
    }
};

2.马拉车

这里借用一个博客里边的图:

具体的马拉车可以参考:

关于马拉车算法_gigi怪的博客-CSDN博客_马拉车算法

首先我们将字符串的每个字符前后都插上一个#,这样,字符串的长度变成了2n-1,比起要比较是否是奇数偶数, 这就方便了许多。

其次,如果当前下标位于一个当前最大回文字符串之内,那么就求出其与中心的距离,上面就是a是中心轴,这个距离是怎么求的呢,这样:

mp[i] = maxpos > i ? min(mp[2 * index - i], maxpos - i) : 1;

马拉车算法(Manacher's Algorithm)_忆南妄北的博客-CSDN博客_马拉车算法

这里面已经把为什么要这样求说的很明白了。

因为我们是要找偏离当前的字符的最远的轴。

总之十分玄乎,我也不明白为什么在对称过去的那个点之前为什么不能存在另一个更远的轴心。

代码如下所示:

class Solution {
public:
    //manacher算法
    void manacher(string& s, int n, vector<int>& mp){ 
        string ms = "";
        ms += "$#";
        //预处理
        for(int i = 0; i < n; i++){ 
            //使之都变成奇数回文子串
            ms += s[i]; 
            ms += '#';
        }
        //目前已知的最长回文子串的最右一位的后一位
        int maxpos = 0; 
        //当前的最长回文子串的中心点
        int index = 0; 
        for(int i = 1; i < ms.length(); i++){  
            mp[i] = maxpos > i ? min(mp[2 * index - i], maxpos - i) : 1; 
            //两边扫
            while(ms[i + mp[i]] == ms[i - mp[i]]) 
                mp[i]++;
            //更新位置
            if(i + mp[i] > maxpos){ 
                maxpos = i + mp[i];
                index = i;
            }
        }
    }
    int getLongestPalindrome(string A) {
        int n = A.length();
        //记录回文子串长度
        vector<int> mp(2 * n + 2); 
        manacher(A, n, mp);
        int maxlen = 0;
        //遍历数组
        for(int i = 0; i < 2 * n + 2; i++) 
            //找到最大的长度
            maxlen = max(maxlen, mp[i] - 1);  
        return maxlen;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值