标签:
鉴于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
[BZOJ2946][Poi2000]公共串解题报告|后缀自动机
标签:
原文地址:http://www.cnblogs.com/mjy0724/p/4478678.html