BZOJ 2946 [Poi2000]公共串 后缀自动机

版权声明:转载请注明:http://blog.csdn.net/YihAN_Z https://blog.csdn.net/YihAN_Z/article/details/62825975

题目大意:给出几个由小写字母构成的单词,求它们最长的公共子串的长度。

后缀自动机练手题。

后缀自动机原理详见CLJ的课件

将一个串建成后缀自动机,其他串放到自动机里去匹配,对于每一个串在每一个状态处记录一下此串在本状态下匹配的最长长度。对于每一个状态,将所有串匹配的最长长度取一个最小即所有串在当前状态下最长的公共子串,用这个长度更新答案即可。

需要注意的地方是匹配到一个位置后要用其更新parent。

#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 2005
#define INF 2000000000
using namespace std;
struct Node {
    Node *pa,*ch[26];
    int val,len[5];
    Node(int _val):val(_val),pa(NULL) {
        memset(ch,0,sizeof ch);
        memset(len,0,sizeof len);
    }
}*root,*last;
void extend(char c) {
    int z=c-'a';
    Node *p=last,*np=new Node(p->val+1);
    last=np;
    while(p && !p->ch[z]) p->ch[z]=np, p=p->pa;
    if(!p) {
        np->pa=root;
        return ;
    }
    Node* q=p->ch[z];
    if(q->val==p->val+1) {
        np->pa=q;
        return ;
    }
    Node* nq=new Node(p->val+1);
    memcpy(nq->ch,q->ch,sizeof q->ch);
    nq->pa=q->pa;
    q->pa=np->pa=nq;
    while(p && p->ch[z]==q) p->ch[z]=nq, p=p->pa;
    return ;
}
void init(char s[]) {
    root=last=new Node(0);
    int len=strlen(s);
    for(int i=0;i<len;i++) extend(s[i]);
    return ;
}
void match(char s[],int x) {
    int len=strlen(s),match_len=0;
    Node* o=root;
    for(int i=0;i<len;i++) {
        int z=s[i]-'a';
        while(o!=root && !o->ch[z]) o=o->pa, match_len=o->val;
        if(o->ch[z]) match_len++, o=o->ch[z];
        Node* tmp=o;
        while(tmp) tmp->len[x]=max(tmp->len[x],min(match_len,tmp->val)), tmp=tmp->pa; //Attention!!
    }
    return ;
}
int n,ans=0;
void find_ans(Node* o) {
    int tmp=INF;
    for(int i=1;i<n;i++) tmp=min(tmp,o->len[i]); //Longest Common Substring in this State
    ans=max(ans,tmp); //Update the answer
    for(int i=0;i<26;i++)
        if(o->ch[i])
            find_ans(o->ch[i]);
    return ;
}
char s[5][N];
int main() {
    scanf("%d",&n);
    for(int i=0;i<n;i++) scanf("%s",s[i]);
    init(s[0]);
    for(int i=1;i<n;i++) match(s[i],i);
    find_ans(root);
    printf("%d\n",ans);
    return 0;
}
阅读更多
换一批

没有更多推荐了,返回首页