题解 | #最长回文子串#

最长回文子串

http://www.nowcoder.com/practice/b4525d1d84934cf280439aeecc36f4af

题解一: 动态规划
图示:dp[i][j]表示A[i:j] 是否为回文串
图片说明
复杂度分析:
时间复杂度:O(N^2)
空间复杂度:O(N^2)
实现如下:

class Solution {
public:
    int getLongestPalindrome(string A, int n) {
        // write code here
        int dp[n][n]; // dp[i][j] 表示A[i:j] 是否是回文串
        int max_len = 0;  //最长回文子串的长度
        memset(dp, 0, sizeof(dp));
        //从尾到头
        for(int i = n-1;i>=0; i--){
            //从左到右
            for(int j = i;j<n;j++){
                int len  = j-i+1; //子串长度
                if(A[i]==A[j]){   // 如果A[i],A[j]相等
                    dp[i][j] = len<=2 ? 1 : dp[i+1][j-1];  //len<=2那么就为回文串,len>2: dp[i][j] = dp[i+1][j-1]
                }
               if(dp[i][j]) max_len = max(max_len,len); //如果A[i:j] 为回文串,那么就从maxlen与len之间取大的
            }
        }
        return max_len;
    }
};

题解二:中心扩散
思路: 以一个字符为中心向两边扩散,找出以该字符为中心的最长回文串。
图示:情况a: 回文串长度为奇数; 情况b:回文串长度为偶数
图片说明

复杂度分析:
时间复杂度:O(N^2)
空间复杂度:O(1)
实现如下:

class Solution {
public:
    //向两边扩散,返回最长回文串长度
    int kuosan(const string &A,int left,int right,const int &n){
        while(left>=0&&right<n&&A[left]==A[right])
        {
            left--; right++;  //向两边扩
        }
        return right-left-1;
    }

    int getLongestPalindrome(string A, int n) {
        int max_len = 0;
        for(int i = n-2;i>=1;i--) //中心位置,注意中心点不会选在0-1之间的中心点,所有循环之后还需要判断中心点选择0-1之间子串
        {
            int len_1 = kuosan(A,i-1,i+1,n); // 回文串长度为奇数的扩散
            int len_2 = kuosan(A,i,i+1,n); // 回文串长度为偶数的扩散,选取i与i+1的中间为扩散开始点
            int len = max(len_1,len_2);  //取奇数长度回文串和偶数长度回文串
            max_len = max(len,max_len);   //最后判断max_len与len大小
        }
        if(A[0]==A[1] && max_len<2) max_len = 2; //判断中心点选择0-1之间子串
        return max_len;
    }
};

题解三:Manacher
题解思路:Manacher利用了回文串的对称性,插入特殊符号将偶回文串变为奇回文串(如:abba----> #a#b#b#a),并且用一个p数组(半径数组)记录字符串中的字符向左向右扩展的长度。
图示:
图片说明
计算半径数组p:
图片说明
复杂度分析:
时间复杂度:O(N) : 只匹配未匹配的位置,对于字符串每个位置只匹配一次。
空间复杂度:O(N)

实现如下:

class Solution {
public:
    void manacher(string s,int n,vector<int>& p){
        int m = 0;
        int m_r = 1; //当前计算回文串的最右端
        for(int i = 1;i<n;i++){
          if(m_r>i) p[i] = min(p[2*m-i],m_r-i); // 情况1 : 解释: 2*m-i;  m到i的距离为i-m. j的位置为 m-(i-m) = 2*m-i;
          for(;(s[i+p[i]]==s[i-p[i]] && (i-p[i]>=0)&&(i+p[i]<2*n+1));p[i]+=1){  // 以i点向两边扩展
            if(m_r < i + p[i]) //情况1中 p[j] > m_r-i
            {
                m_r = i+p[i];  //更新最右端值
                m = i; //更新m
            }
            }
        }
    }
    int getLongestPalindrome(string A, int n) {
        int max_len = 0;
        string s="";   // 存储插入特殊字符之后的字符串
        vector<int> p(2*n+1,1);
        for(int i = 0;i<2*n+1;i++)  //插入特殊字符
        {
           s.push_back('#');  
           s.push_back(A[i/2]);++i; 
        }
        manacher(s, 2*n+1, p);
        for(auto i : p){
            max_len = max(max_len,i-1);  //取最大的
        }
        return max_len;
    }
};
牛客网编程题题解 文章被收录于专栏

本专栏记录在牛客网上AC的每一题,写下题解。 未来2年完成2000编程题的题解。 2021.12.29更新,最进准备毕设,断更了,会尽快做完毕设,继续做这一件事情

全部评论
dalao,你的题解二:中心扩散法中对扩撒的判断应该是有问题的; 首先肯定是A[left] == A{right]之后再向两边扩充; 其次如果是奇数的情况,如果第一次进入扩散判断就不相等,此时回文串的长度应该为1不是right-left+1
点赞
送花
回复
分享
发布于 2021-08-19 21:37
方法二确实有问题呦
点赞
送花
回复
分享
发布于 2021-08-29 12:44
滴滴
校招火热招聘中
官网直投

相关推荐

4 3 评论
分享
牛客网
牛客企业服务