2

问题描述

回文串是指aba、abba、cccbccc、aaaa这种左右对称的字符串。
输入一个字符串Str,输出Str里最长回文子串的长度。

方法一:暴力求解

遍历每一个子串,再判断这个子串是不是回文串,最后判断这个串是不是最长的回文子串。
遍历子串的复杂度是O(n^2),判断是不是回文串的复杂度是O(n),所以这个算法的复杂度是O(n^3)。

方法二:动态规划法

用一个二维的数组ai来表示从第i位到第j位的子串是不是回文串,在判断从i到j的子串是不是回文串时,可以先看i+1到j-1是不是回文串,再判断i位和j位是不是相同。这个算法中,遍历子串的复杂度仍然是O(n^2),但是判断是不是回文串的复杂度降到了O(1),所以这个算法的复杂度是O(n^2)。但是这个算法占据了O(n^2)的空间。

方法三:中心扩展法

顾名思义,任何一个回文串都有一个对称轴,从这个中心的位置开始,向两边扩展,可以得到以此为中心的最长回文串。但是要注意,这个对称轴的位置,可能是一个字符,也可能是两个字符中间。遍历对称轴的位置,复杂度是O(n),找到以此对称轴为中心的最长回文串,其复杂度是O(n),所以此算法的复杂度是O(n^2)。这个算法比动态规划好的地方是其空间复杂度只有O(1)。

#include <iostream>
#include <cstring>
using namespace std;

#define LEN 1000

int main(){
    char str[LEN];
    cin>>str;
    int len=strlen(str);
    int maxlen=0,mx;
    for(int i=0;i<len;i++){
        mx=1;
        for(int j=1;(i-j>=0)&&(i+j<len);j++){
            if(str[i-j]==str[i+j])
                mx+=2;
            else break;
        }
        maxlen=maxlen>mx?maxlen:mx;
    }
    for(int i=0;i<len;i++){
        mx=0;
        for(int j=0;(i-j>=0)&&(i+j+1<len);j++){
            if(str[i-j]==str[i+j+1])
                mx+=2;
            else break;
        }
        maxlen=maxlen>mx?maxlen:mx;
    }
    cout<<maxlen<<endl;
    return 0;
}

方法四:manacher算法

预处理

在字符串的开始加上一个'$'符,然后在每个字符中间插上一个'#'。比如,字符串ss='abac',处理之后是str='$#a#b#a#c#'。接下来的计算针对处理后的字符串。

len数组

然后定义一个len数组,len[i]表示的是以str[i]为中心的最长回文串的半径。
仍以上面的字符为例。str='$#a#b#a#c#',以str[0]为中心的最长回文串是'$',其半径是1;以str[4]为中心的最长回文串是'#a#b#a#',其半径是4;len数组为{1,1,2,1,4,1,2,1,2,1}。可以发现,len[i]-1的值,就是原字符串ss中对应的回文串的长度(以#为中心的是偶长度的回文串,以字符为中心的是奇长度的回文串)。

计算len数组

算法的关键在于在计算len数组时,可以利用前面的结果进行优化。
引入变量maxright表示当前访问到的所有回文子串,所能触及的最右一个字符的位置;同时记录maxright所对应的回文串的对称轴的位置,记为pos。

复杂度分析

考虑p的值的变化,在计算的过程中,p只会增加不会减少,当p增加到strlen(str)时,每个位置的len数组的值都可以立即计算得出。所以算法的复杂度是O(n)。

#include <iostream>
#include <string>
#include <cstring>
using namespace std;

#define N 100004

string str,ss;
int len[2*N+1];

int main()
{
    cin>>ss;
    str="$#";
    for(unsigned int i=0;i<ss.size();i++)
    {
        str+=ss[i];
        str+="#";
    }
//  cout<<str<<endl;

    int pos=0,p=0,j=0;
    len[0]=1;
    for(unsigned int i=1;i<str.size();i++)
    {
        j=2*pos-i;
        if(p>i)
            len[i]=(len[j]>p-i)?(p-i):(len[j]);
        else
            len[i]=1;
        while(i+len[i]<str.size()&&i-len[i]>=0&&str[i+len[i]]==str[i-len[i]])
            len[i]++;
        if(i+len[i]>=p)
        {
            pos=i;
            p=i+len[i];
        }
    }
    int ans=0;
    for(int i=0;i<str.size();i++)
    {
//      cout<<len[i]<<" ";
        ans=(len[i]-1>=ans)?len[i]-1:ans;
    }
//  cout<<endl;
    cout<<ans<<endl;
    return 0;
}

cppowboy
20 声望1 粉丝