Leetcode 5. Longest Palindromic Substring

Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.

Example:

Input: “babad”

Output: “bab”

Note: “aba” is also a valid answer.
Example:

Input: “cbbd”

Output: “bb”

思路:
1. 简单直接的做法:以每个字符为中心,往两侧扩展找最长的palindromic substring,然后取最大值!复杂度worst case O(n^2)
2. 最快的方法o(n)。manacher’s algorithm。o(n)的算法是由于减少了大量重复不必要的计算。算法本身有点复杂。试举例说明:
Given string:

babababcbaccba

pre-processing:每个符号两边插入一个特殊符号,这样找palindrome就把奇对称和偶对称统一成一种情况了。

0123456789 10 11  13   15  17
#b#a#b#a#b  #  a # b #  c # b#a#c#c#b#a#

从左往右遍历,把每个字符对应的最长的palindrome的长度保存下来。即:空间是o(n)。如上图,在i=5时的最长的palindrome是#b#a#b#a#b,说明以5为中心,两侧对称,这里可以推论一个性质:

关于某个点对称的两个位置的环境相似,即:有有相同的parlindrome,所以长度相同。

所以,i=7和i=3是关于i=5对称,所以i=7的parlindrome和i=3一样,都是:#b#a#b#。注意,能应用这个推论的前提是,i=7和i=3这两个点都在i=5为中心的最长parlindrome的范围之内,即:[0,9].为了用编程或数学语言描述这个parlindrome,需要用两个参数:一种是直接用这个parlindrome的开头和结尾位置;一种是用该parlindrome的center位置和最right的点的位置。这里选用后者!
以上只是一般的情况,在极限的情况下,由于添加了新的界限,需要相应的调整parlindrome的长度。
A. 当right-i < p[mirror],说明此时p[i]最多只能等于right-i, 因为right是现在parlindrome的边界。这里用了减法,需要用right-i>0对前面的条件做保护。
B. 一旦p[i]根据上面的条件确定了,只能说明这个p[i]是在当前的center和right两个参数限定的parlindrome里的sub parlindrome的长度,不一定代表在i处的最长的parlindrome,因为一旦允许跨越这个限定就可能更长。所以,必须比较判定while(T[i+1+p[i]]==T[i-1-p[i]]),如果成立,继续expand p[i]。

现在总结一下整个流程:
1. 在每个位置i处,根据当前的center和right限定的parlindrome,由i关于center对称的i_mirror位置的p[i] (parlindrome长度)
2. 初步得到p[i],还需要进一步检查是否能超出当前center和right限定的parlindrome的范围而扩展以i为中心的parlindrome。
3. 判断是否需要更新center和right的值,如果i+p[i]>right的话
4. 更新最长的parlindrome的center和right

讨论:
1.为什么这个看起来复杂的方法仍然是o(n)? 是因为每个坐标处只是扩展的时候参与运算一次,所以每个点使用一次。

//方法1:O(n)
class Solution {
public:

    string preprocess(string&s){
        string t;
        for(auto k:s)
            t=t+'#'+k;
        t+='#';
        return t;
    }
    //Preprocess另一种快速方法:比上面快,是因为在初始化的时候就把所有元素赋值'#',然后把偶数位置替换成字母。
    string preprocess(string&s){
        int len=2*s.size()+1;
        string t(len,'#');
        int m=0;
        for(int i=1;i<len;i+=2)
            t[i]=s[m++];
        return t;
    }

    string longestPalindrome(string s) {
        //
        string T=preprocess(s);
        int n=T.size();
        vector<int> P(n);
        int center=0,right=0;//初始化parlindrome为空
        int mxLen=0,idx=0;
        for(int i=0;i<n;i++){
            int mirror=2*center-i;//找到对称的位置
            //在当前限定的parlindrome找i为中心的parlindrome的长度
            P[i]=right>i?min(right-i,P[mirror]):0;
            while(i+1+P[i]<n&&i-1-P[i]>=0&&T[i+1+P[i]]==T[i-1-P[i]])
                P[i]++;//扩展p[i]
            if(i+P[i]>right){//更新center和right
                center=i;
                right=i+P[i];
            }
            if(P[i]>mxLen){
                mxLen=P[i];
                idx=i;
            }
        }   
        return s.substr((idx-mxLen)/2,mxLen); 
    }
};


//方法2:o(n^2),奇对称和偶对称都考虑。
class Solution {
public:
    void helper(string&s,int left, int right,int mxLen, int start){
        while(left>=0&&right<s.size()&&s[left]==s[right])
            left--;
            right++;
        if(mxLen<right-left-1)//
        {
            mxLen=right-left-1;
            start=left+1;
        }   
    }

    string longestPalindrome(string s) {
        int mxLen=1,start=0;
        for(int i=0;i<s.size()-1;i++){
            helper(s,i,i,mxLen,start);
            helper(s,i,i+1,mxLen,start);
        }
        return s.substr(start,mxLen);
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值