SPOJ-LCS 后缀自动机

题面传送门

题意:给出两个$n=250000$的字符串,求最长公共子串。

思路:把第一个字符串放到SAM里面去。

      对于第二个串,能匹配则匹配,不能匹配就跳回能匹配的位置,如果一个都不能匹配,则cur要重新初始化为root。

           对后缀自动机的理解还比较模糊,怕误人子弟,此题就不做详述。

#include<bits/stdc++.h>
#define clr(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=250010*2;
char s1[maxn],s2[maxn];
int len1,len2,len[maxn],tot=1,root=1,last=1,ch[maxn][27],fa[maxn];
void extend(int x){
    int now=++tot,pre=last;
    last=now,len[now]=len[pre]+1;
    while( pre && !ch[pre][x]){
        ch[pre][x]=now;
        pre=fa[pre];
    }
    if(!pre)fa[now]=root;
    else{
        int q = ch[pre][x];
        if(len[q]==len[pre]+1)fa[now]=q;
        else{
            int nows = ++tot;
            memcpy(ch[nows],ch[q],sizeof(ch[q]));
            len[nows]=len[pre]+1;
            fa[nows]=fa[q];
            fa[q]=fa[now]=nows;
            for(;pre && ch[pre][x] == q;pre=fa[pre]){
                ch[pre][x]=nows;
            }
        }
    }
} 
int main(){
    scanf("%s",s1);
    scanf("%s",s2);
    len1=strlen(s1);
    len2=strlen(s2);
    for(int i=0;i<len1;i++)
    {
        int p=s1[i]-'a';
        extend(p);
    }
    int maxx=0,cur=root,ans=0;
    for(int i=0;i<len2;i++,ans=max(maxx,ans))
    {
        int p=s2[i]-'a';
        if(ch[cur][p]){
            cur=ch[cur][p];
            maxx++;
            continue;
        }
        while(cur&&ch[cur][p]==0){
            cur=fa[cur];
        }
        if(cur){
            maxx=len[cur]+1;
            cur=ch[cur][p];
        }else{
            cur=1;
            maxx=0;
        }
    }
    printf("%d\n",ans);
}

 

posted @ 2019-04-06 13:33  光芒万丈小太阳  阅读(267)  评论(0编辑  收藏  举报