[BZOJ2946] [Poi2000]公共串解题报告|后缀数组
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。单词个数<=5,每个单词长度<=2000
尽管最近在学的是SAM...但是看到这个题还是忍不住想写SA...
(其实是不知道应该怎么用SAM做...
对于后缀数组而言,多个字符串的公共子串与两个处理起来并没有什么区别
只要在中间加一些没有用的字符,将多个字符串拼成一个字符串
然后二分答案,对于一个长度L,在一组除了开头其他height都>=L的区间中如果每个字符串的位置都出现过就可以
应该是第二次这么解决一道公共串的题了..
然后发现了一些新的东西..
比如之前处理没有的字符串处理了很久,这次我直接在每两个字符串间添加不同的字符
理由很简单:
对于原来就在同一个字符串中的两个后缀,它们后面的字符相同,但是由于长度不同,所以没有影响
即匹配的时候谁先接触到无用字符谁就更小一些,符合我们的愿望
而对于原来不在一个字符串中的两个后缀,如果他们后面的字符相同,再加上原本的长度也一样
匹配的时候height数组算出的答案就会比实际的长度要大
然而我们现在填了不同的字符就可以很好地避免掉这个问题
不知道为什么...我的SA很长...
不过无所谓只要好理解不用背代码...长一点也只是敲敲键盘的问题...
这里是用SAM解这道题的方法
1 program bzoj2946; 2 const maxn = 30010; 3 var b,sa,rank,tmp,a,pos,height:array[-1..maxn]of longint; 4 vis,l,r:array[-1..10]of longint; 5 maxlen,n,m,i,j,lx,rx,mid,ans:longint; 6 s:ansistring; 7 ss:array[-1..10]of ansistring; 8 9 10 function max(a,b:longint):longint; 11 begin 12 if a>b then exit(a) else exit(b); 13 end; 14 15 procedure Suffix_Array; 16 var i,j,p,sz,v0,v1,v01,v00:longint; 17 begin 18 sz:=max(200,length(s)); 19 for i:=0 to sz do b[i]:=0; 20 for i:=0 to m-1 do inc(b[a[i]]); 21 for i:=0 to m-1 do rank[i]:=a[i]; 22 for i:=1 to sz do inc(b[i],b[i-1]); 23 for i:=m-1 downto 0 do 24 begin 25 dec(b[rank[i]]); 26 sa[b[rank[i]]]:=i; 27 end; 28 j:=1; 29 while j<=m do 30 begin 31 p:=0; 32 for i:=m-j to m-1 do 33 begin 34 tmp[p]:=i;inc(p); 35 end; 36 for i:=0 to m-1 do if sa[i]-j>=0 then 37 begin 38 tmp[p]:=sa[i]-j;inc(p); 39 end; 40 for i:=0 to sz do b[i]:=0; 41 for i:=0 to m-1 do inc(b[rank[i]]); 42 for i:=1 to sz do inc(b[i],b[i-1]); 43 for i:=m-1 downto 0 do 44 begin 45 dec(b[rank[tmp[i]]]); 46 sa[b[rank[tmp[i]]]]:=tmp[i]; 47 end; 48 tmp[sa[0]]:=0;p:=0; 49 for i:=1 to m-1 do 50 begin 51 v0:=sa[i-1];v1:=sa[i]; 52 if v0+j<m then v00:=rank[v0+j] else v00:=-1; 53 if v1+j<m then v01:=rank[v1+j] else v01:=-1; 54 if (rank[v0]=rank[v1])and(v00=v01) then tmp[sa[i]]:=p else 55 begin 56 inc(p);tmp[sa[i]]:=p; 57 end; 58 end; 59 for i:=0 to m-1 do rank[i]:=tmp[i]; 60 j:=j << 1; 61 end; 62 end; 63 64 65 function compare(i,j,x:longint):longint; 66 begin 67 while (i+x-1<m)and(j+x-1<m)and(a[i+x-1]=a[j+x-1]) do inc(x); 68 exit(x-1); 69 end; 70 71 procedure calc_height; 72 var i:longint; 73 begin 74 if rank[0]=0 then height[0]:=0 else height[0]:=compare(0,sa[rank[0]-1],1); 75 for i:=1 to m-1 do 76 if rank[i]=0 then height[i]:=0 else height[i]:=compare(i,sa[rank[i]-1],max(height[i-1],1)); 77 end; 78 79 function solve(x:longint):boolean; 80 var i,j,k:longint; 81 begin 82 for i:=0 to n do vis[i]:=-1; 83 i:=0; 84 while i<m do 85 begin 86 j:=i+1; 87 while (j<m)and(height[sa[j]]>=x) do inc(j); 88 for k:=i to j-1 do vis[pos[sa[k]]]:=i; 89 for k:=1 to n do if vis[k]<>i then break; 90 if vis[k]=i then exit(true); 91 i:=j; 92 end; 93 exit(false); 94 end; 95 96 begin 97 readln(n);s:='';maxlen:=0; 98 for i:=1 to n do 99 begin 100 readln(ss[i]); 101 if length(ss[i])>maxlen then maxlen:=length(ss[i]); 102 end; 103 maxlen:=1 << (trunc(ln(maxlen)/ln(2))+1); 104 fillchar(pos,sizeof(pos),0); 105 for i:=1 to n do 106 begin 107 l[i]:=length(s)+1; 108 s:=s+ss[i]; 109 r[i]:=length(s); 110 for j:=l[i] to r[i] do pos[j-1]:=i; 111 for j:=1 to maxlen do s:=s+chr(i); 112 end; 113 m:=length(s); 114 for i:=0 to m-1 do a[i]:=ord(s[i+1]); 115 Suffix_Array; 116 Calc_Height; 117 Lx:=1;Rx:=maxlen;ans:=0; 118 while Lx<=Rx do 119 begin 120 mid:=(Lx+Rx) >> 1; 121 if solve(mid) then 122 begin 123 ans:=mid;Lx:=mid+1; 124 end else Rx:=mid-1; 125 end; 126 writeln(ans); 127 end.