COJ977 WZJ的数据结构(负二十三)

试题描述

输入一个字符串S,输出S的最长连续回文子串长度。

输入
输入一个字符串S。
输出
输出S的最长连续回文子串长度
输入示例
abacbbc
输出示例
4
其他说明
1<=|S|<=1000000

这就是传说中的萌萌哒马拉车算法(manacher)啦

首先为了方便处奇偶两种情况,将S重新变成新的字符串T,如abacddc变成#a#b#a#c#d#d#c#

再次为了方便处理越界,将字符串首尾加一个奇怪的不匹配字符,如将abacddc变成~#a#b#a#c#d#d#c#`

请大家想一想这样的好处

考虑暴力算法,枚举回文串中心,暴力向两边匹配

rep(1,n-1) {
    int t=1;
    while(s[i-t]==s[i+t]) t++;
    ans=max(ans,t-1);
}

这样是O(N^2),考虑优化

我们定义P[i]表示位置i的最长匹配长度,考虑利用之前的匹配信息。

这样就是p[i]=p[2*id-i]

 

这样就是p[i]=mx-i

写成代码就是这样

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(s,t) for(int i=s;i<=t;i++)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=2000010;
char s[maxn];
int p[maxn];
int solve(char* s2) {
    int n=1,id=0,mx=0,ans=0;
    for(int i=0;s2[i]!='\0';i++) s[n++]='#',s[n++]=s2[i];
    s[0]='~';s[n++]='#';s[n++]='`';
    rep(1,n-1) {
        if(mx>i) p[i]=min(p[2*id-i],mx-i);
        else p[i]=1;
        while(s[i+p[i]]==s[i-p[i]]) p[i]++;
        if(i+p[i]>mx) mx=i+p[i],id=i;
        ans=max(ans,p[i]-1);
    }
    return ans;
}
char s2[maxn];
int main() {
    scanf("%s",s2);
    printf("%d\n",solve(s2));
    return 0;
}
View Code

为什么是O(N)的呢?

因为算法只有遇到还没有匹配的位置时才进行匹配,已经匹配过的位置不再进行匹配,所以对于T字符串中的每一个位置,只进行一次匹配,所以Manacher算法的总体时间复杂度为O(n),其中n为T字符串的长度,由于T的长度事实上是S的两倍,所以时间复杂度依然是线性的。

补一个PAM的(以后不能装*,将”f[np]=to[k][c];to[p][c]=np;“i写成”f[to[p][c]=np]=to[k][c];“就会T飞,因为k可能等于p)

#include<cstdio>
#include<cctype>
#include<queue>
#include<cstring>
#include<algorithm>
#define rep(s,t) for(int i=s;i<=t;i++)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const int maxn=1000010;
char ch[maxn];
struct PAM {
    int cnt,last;
    int to[maxn][26],f[maxn],l[maxn];
    PAM() {cnt=f[0]=1;l[1]=-1;}
    void extend(int c,int n) {
        int p=last;
        while(ch[n]!=ch[n-l[p]-1]) p=f[p];
        if(!to[p][c]) {
            int np=++cnt,k=f[p];l[np]=l[p]+2;
            while(ch[n]!=ch[n-l[k]-1]) k=f[k];
            f[np]=to[k][c];to[p][c]=np;
        }
        last=to[p][c];
    }
    int solve() {
        int ans=0;
        rep(1,cnt) ans=max(ans,l[i]);
        return ans;
    }
}sol;
int main() {
    scanf("%s",ch+1);int n=strlen(ch+1);
    rep(1,n) sol.extend(ch[i]-'a',i);
    printf("%d\n",sol.solve());
    return 0;
}
View Code

 

posted @ 2015-07-03 11:12  wzj_is_a_juruo  阅读(223)  评论(0编辑  收藏  举报