[BZOJ2946][Poi2000]公共串解题报告|后缀自动机

  鉴于SAM要简洁一些...于是又写了一遍这题...

  不过很好呢又学到了一些新的东西...

  这里是用SA做这道题的方法

 

  首先还是和两个字符串的一样,为第一个字符串建SAM

  然后每一个字符串再在这个SAM上跑匹配

  然而我们最后要的答案是什么呢?

  是某个在所有字符串中匹配长度最小值最大的状态子串

  然后对于每一个字符串

  我们可以记录它在每一个状态子串上的最大匹配长度

  最后需要一个非常关键的转移

  就是用当前节点的值更新fail指针指向的节点

 

  比如这种情况

  如果一次匹配到左边的三个节点,一次匹配到右边的两个节点(两次匹配在不同的字符串中)

  那么显然,这两个字符串的公共子串长度为2是存在的

  但是由于我们没有转移过,fail指针指向的点没有储存前面的信息就会出错

  

  然后至于转移的顺序,我们可以按照深度顺序

  这个可以用桶排实现

 

 

 1 program bzoj2946;
 2 const maxn = 100010;
 3 var n,i,j,now,maxl,root,c,tot,cnt,t:longint;
 4     s:array[1..5]of ansistring;
 5     mx,fail,q,b,ans,tem:array[-1..maxn]of longint;
 6     a:array[-1..maxn,-1..30]of longint;
 7 
 8 function max(a,b:longint):longint;
 9 begin
10     if a>b then exit(a) else exit(b);
11 end;
12 
13 function min(a,b:longint):longint;
14 begin
15     if a<b then exit(a) else exit(b);
16 end;
17 
18 function insert(p,c:longint):longint;
19 var np,q,nq:longint;
20 begin
21     inc(cnt);np:=cnt;mx[np]:=mx[p]+1;
22     while (p<>0)and(a[p,c]=0) do
23     begin
24         a[p,c]:=np;p:=fail[p];
25     end;
26     if p=0 then fail[np]:=root else
27     begin
28         q:=a[p,c];
29         if mx[q]=mx[p]+1 then fail[np]:=q else
30         begin
31             inc(cnt);nq:=cnt;mx[nq]:=mx[p]+1;
32             a[nq]:=a[q];
33             fail[nq]:=fail[q];
34             fail[np]:=nq;fail[q]:=nq;
35             while a[p,c]=q do
36             begin
37                 a[p,c]:=nq;p:=fail[p];
38             end;
39         end;
40     end;
41     exit(np);
42 end;
43 
44 begin
45     readln(n);
46     for i:=1 to n do readln(s[i]);
47     cnt:=1;root:=1;t:=root;
48     for i:=1 to length(s[1]) do t:=insert(t,ord(s[1,i])-97);
49     for i:=2 to cnt do ans[i]:=mx[i];
50     fillchar(b,sizeof(b),0);
51     for i:=2 to cnt do inc(b[mx[i]]);
52     for i:=2 to cnt do inc(b[i],b[i-1]);
53     for i:=2 to cnt do ans[i]:=mx[i];
54     for i:=cnt downto 2 do
55     begin
56         dec(b[mx[i]]);
57         q[b[mx[i]]]:=i;
58     end;
59     for i:=1 to n do
60     begin
61         now:=root;maxl:=0;
62         fillchar(tem,sizeof(tem),0);
63         for j:=1 to length(s[i]) do
64         begin
65             c:=ord(s[i][j])-97;
66             if a[now,c]<>0 then begin now:=a[now,c];inc(maxl);end else
67             begin
68                 while (now<>0)and(a[now,c]=0) do now:=fail[now];
69                 if now=0 then begin now:=root;maxl:=0;end else begin maxl:=mx[now]+1;now:=a[now,c];end;
70             end;
71             tem[now]:=max(tem[now],maxl);
72         end;
73         for j:=cnt downto 0 do tem[fail[q[j]]]:=max(tem[fail[q[j]]],tem[q[j]]);
74         for j:=2 to cnt do ans[j]:=min(ans[j],tem[j]);
75     end;
76     tot:=0;
77     for i:=2 to cnt do if ans[i]>tot then tot:=ans[i];
78     writeln(tot);
79 end.

 

 

 

  比较了一下..代码减少了三分之一,空间缩小了十分之九...最主要写起来简单多了

  SAM大法好

 

 

  

  05/.May

posted @ 2015-05-05 11:21  mjy0724  阅读(488)  评论(0编辑  收藏  举报