最长回文子串

回文串:如果一个字符串正着读和反着读是一样的,那么它就是一个回文串。例如,a,aba,abba,aaaa等都是回文串。
最长回文子串问题是指,给定一个字符串,求出这个字符串中最长的连续的回文子串。
最长回文子串问题容易与最长回文序列问题混淆,最长回文字串是在给定字符串中连续的回文子串,而最长回文序列问题中可以不连续。例如,给定一个字符串abcbdda,这个字符串的最长回文子串是abc或bcb,而最长回文子序列为abba或adda。

最长回文子串解法一:暴力搜索
对于最长回文子串问题,最容易想到的方法也是最简单粗暴的解法就是,遍历字符串所有子串,并确定每一个子串是否为回文串。一个字符串中,它的字串的数量为O(n^2),遍历每个子串需要O(n)的时间,因此,这个解法的时间复杂度为O(n^3)

最长回文字符串解法二:动态规划
可能有很多人像我一样会想到,这是一个动态规划问题。判断动态规划问题的两个显著条件就是:一.原始问题具有最优子结构。二.子问题的解有重叠
首先我们可以将这个问题分解为子问题,即将一个字符串s分解为O(n^2)个子串,然后判断每个子串是否为一个回文串,并找到最长的回文子串。
假如s[i,j]是一个回文串(i-j>=2),那么s[i+1,j-1]一定也是一个回文串,那么对于子问题:判断s[i,j] (i-j>=2)是否是一个回文串,那么我们首先可以判断s[i+1,j-1]是否为一个回文串,1.如果s[i+1,j-1] 是回文串,那么我们接下来可以根据s[i]是否等于s[j]来判断s[i,j]是否为回文串,2.如果s[i+1,j-1]不是回文串,那么s[i,j]一定也不是回文串。以record[i,j]记录s[i,j]是否为回文串,则有:

record[i,j]=true,i=jtrue,j=i+1 and s[i]=s[j]true,s[i]=s[j] and record[i+1,j1]=truefalse,otherwise

string longestPalindrome(string s) {
        int length=s.length();
        vector<vector<bool>> record(length,vector<bool>(length,false));
        for(int i=0;i<length;++i)
            record[i][i]=true;
        for(int l=1;l<length;++l){ //先解决字符串长度为l+1的最优子问题
            for(int i=0;i+l<length;++i){ //i是子字符串起始位置
                if(s[i]==s[i+l]){
                    if(l==1)
                        record[i][i+l]=true;
                    else if(record[i+1][i+l-1]==true)
                        record[i][i+l]=true;
                }
            }
        }

        int start=0;
        int end=0;
        int maxLen=0;
        for(int i=0;i<length;++i)
            for(int j=i;j<length;++j){
                if(record[i][j]==true && j-i+1>maxLen){
                    start=i;
                    end=j;
                    maxLen=j-i+1;
                }
            }

        return s.substr(start,end-start+1);
    }

动态规划算法的时间复杂度为O(n^2),空间复杂度为O(n^2)。
那么有没有复杂度更低的算法呢?

最长回文子串解法三:Manacher 算法
Manacher算法讲解的非常详细,时间复杂度为O(n)。
程序如下:

 string longestPalindrome(string s){
        //Manacher法
        int length=s.length();

        string pad_s;
        pad_s.resize(2*length+1);
        for(int i=length-1;i>=0;--i){
            pad_s[2*i+2]='#';
            pad_s[2*i+1]=s[i];
        }
        pad_s[0]='*'; //防止在下面的while循环中出界

        vector<int> r(2*length+1,0);
        int center=0;
        int maxStart=0,maxEnd=0,maxLen=0;
        for(int i=1;i<2*length+1;++i){
            if(center+r[center]>i)
                r[i]=min(r[center]-(i-center),r[center-(i-center)]);
            else 
                r[i]=1;
            while(pad_s[i+r[i]]==pad_s[i-r[i]])
                ++r[i];
            if(i+r[i]>center+r[center])
                center=i;
            if(r[i]>maxLen){
                maxLen=r[i];
                maxStart=i-r[i]+1;
                maxEnd=i+r[i]-1;
            }
        }

        string result;
        for(int k=maxStart;k<=maxEnd;++k){
            if(pad_s[k]!='#')
                result.push_back(pad_s[k]);
        }
        return result;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值