SPOJ 1812 LCS2 后缀自动机

题意:

给10个字符串,求他们的最长公共子串。

 

题解:

只要明确后缀自动机中的每个节点都是其实都对应着逆序的后缀树的一个集合。

而nlcs(当前最长匹配长度)和lcs都是对于这个集合而言的,所有,每个点都要更新他的*f指针(逆序后缀树的父亲)所指的节点。

 

 

View Code
 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <algorithm>
 5 #include <cstdio>
 6 
 7 #define N 500050
 8 
 9 using namespace std;
10 
11 struct SAM
12 {
13     SAM *son[26],*f;
14     int l,nlcs,lcs;//当前lcs,之前所有串的lcs 
15 }sam[N],*b[N],*head,*last;
16 
17 int len,ans,cnt;
18 char str[N];
19 int d[N];
20 
21 inline void add(int x)
22 {
23     SAM *p=&sam[++cnt],*jp=last;
24     p->l=p->lcs=last->l+1; last=p;
25     for(;jp&&!jp->son[x];jp=jp->f) jp->son[x]=p;
26     if(!jp) p->f=head;
27     else if(jp->l+1==jp->son[x]->l) p->f=jp->son[x];
28     else
29     {
30         SAM *r=&sam[++cnt],*q=jp->son[x];
31         *r=*q; r->l=r->lcs=jp->l+1;
32         p->f=q->f=r;
33         for(;jp&&jp->son[x]==q;jp=jp->f) jp->son[x]=r;
34     }
35 }
36 
37 inline void read()
38 {
39     scanf("%s",str);
40     len=strlen(str);
41     head=last=&sam[cnt=0];
42     for(int i=0;i<len;i++) add(str[i]-'a');
43 }
44 
45 inline void go()
46 {
47     for(int i=0;i<=cnt;i++) d[sam[i].l]++;
48     for(int i=1;i<=len;i++) d[i]+=d[i-1];
49     for(int i=0;i<=cnt;i++) b[--d[sam[i].l]]=&sam[i];
50     while(scanf("%s",str)!=EOF)
51     {
52         len=strlen(str);
53         for(int i=0;i<len;i++) str[i]-='a';
54         int res=0; last=head;
55         for(int i=0;i<len;i++)
56         {
57             if(last->son[str[i]])
58             {
59                 res++;
60                 last=last->son[str[i]];
61                 last->nlcs=max(res,last->nlcs);
62             }
63             else
64             {
65                 for(;last&&!last->son[str[i]];last=last->f);
66                 if(!last) res=0,last=head;
67                 else
68                 {
69                     res=last->l+1;
70                     last=last->son[str[i]];
71                     last->nlcs=max(res,last->nlcs);
72                 }
73             }
74         }
75         for(int i=cnt;i>0;i--)
76         {
77             b[i]->lcs=min(b[i]->nlcs,b[i]->lcs);
78             b[i]->f->nlcs=max(b[i]->f->nlcs,b[i]->nlcs);
79             b[i]->nlcs=0;
80         }
81     }
82     ans=0;
83     for(int i=0;i<=cnt;i++) ans=max(ans,sam[i].lcs);
84     printf("%d\n",ans);
85 } 
86 
87 int main()
88 {
89     read(),go();
90     return 0;
91 } 

 

 

posted @ 2013-02-20 23:33  proverbs  阅读(898)  评论(0编辑  收藏  举报