[BZOJ2946] [Poi2000]公共串解题报告|后缀数组
2015-05-05 09:35
465 查看
给出几个由小写字母构成的单词,求它们最长的公共子串的长度。
单词个数<=5,每个单词长度<=2000
尽管最近在学的是SAM...但是看到这个题还是忍不住想写SA...
(其实是不知道应该怎么用SAM做...
对于后缀数组而言,多个字符串的公共子串与两个处理起来并没有什么区别
只要在中间加一些没有用的字符,将多个字符串拼成一个字符串
然后二分答案,对于一个长度L,在一组除了开头其他height都>=L的区间中如果每个字符串的位置都出现过就可以
应该是第二次这么解决一道公共串的题了..
然后发现了一些新的东西..
比如之前处理没有的字符串处理了很久,这次我直接在每两个字符串间添加不同的字符
理由很简单:
对于原来就在同一个字符串中的两个后缀,它们后面的字符相同,但是由于长度不同,所以没有影响
即匹配的时候谁先接触到无用字符谁就更小一些,符合我们的愿望
而对于原来不在一个字符串中的两个后缀,如果他们后面的字符相同,再加上原本的长度也一样
匹配的时候height数组算出的答案就会比实际的长度要大
然而我们现在填了不同的字符就可以很好地避免掉这个问题
不知道为什么...我的SA很长...
不过无所谓只要好理解不用背代码...长一点也只是敲敲键盘的问题...
这里是用SAM解这道题的方法
program bzoj2946; const maxn = 30010; var b,sa,rank,tmp,a,pos,height:array[-1..maxn]of longint; vis,l,r:array[-1..10]of longint; maxlen,n,m,i,j,lx,rx,mid,ans:longint; s:ansistring; ss:array[-1..10]of ansistring; function max(a,b:longint):longint; begin if a>b then exit(a) else exit(b); end; procedure Suffix_Array; var i,j,p,sz,v0,v1,v01,v00:longint; begin sz:=max(200,length(s)); for i:=0 to sz do b[i]:=0; for i:=0 to m-1 do inc(b[a[i]]); for i:=0 to m-1 do rank[i]:=a[i]; for i:=1 to sz do inc(b[i],b[i-1]); for i:=m-1 downto 0 do begin dec(b[rank[i]]); sa[b[rank[i]]]:=i; end; j:=1; while j<=m do begin p:=0; for i:=m-j to m-1 do begin tmp[p]:=i;inc(p); end; for i:=0 to m-1 do if sa[i]-j>=0 then begin tmp[p]:=sa[i]-j;inc(p); end; for i:=0 to sz do b[i]:=0; for i:=0 to m-1 do inc(b[rank[i]]); for i:=1 to sz do inc(b[i],b[i-1]); for i:=m-1 downto 0 do begin dec(b[rank[tmp[i]]]); sa[b[rank[tmp[i]]]]:=tmp[i]; end; tmp[sa[0]]:=0;p:=0; for i:=1 to m-1 do begin v0:=sa[i-1];v1:=sa[i]; if v0+j<m then v00:=rank[v0+j] else v00:=-1; if v1+j<m then v01:=rank[v1+j] else v01:=-1; if (rank[v0]=rank[v1])and(v00=v01) then tmp[sa[i]]:=p else begin inc(p);tmp[sa[i]]:=p; end; end; for i:=0 to m-1 do rank[i]:=tmp[i]; j:=j << 1; end; end; function compare(i,j,x:longint):longint; begin while (i+x-1<m)and(j+x-1<m)and(a[i+x-1]=a[j+x-1]) do inc(x); exit(x-1); end; procedure calc_height; var i:longint; begin if rank[0]=0 then height[0]:=0 else height[0]:=compare(0,sa[rank[0]-1],1); for i:=1 to m-1 do if rank[i]=0 then height[i]:=0 else height[i]:=compare(i,sa[rank[i]-1],max(height[i-1],1)); end; function solve(x:longint):boolean; var i,j,k:longint; begin for i:=0 to n do vis[i]:=-1; i:=0; while i<m do begin j:=i+1; while (j<m)and(height[sa[j]]>=x) do inc(j); for k:=i to j-1 do vis[pos[sa[k]]]:=i; for k:=1 to n do if vis[k]<>i then break; if vis[k]=i then exit(true); i:=j; end; exit(false); end; begin readln(n);s:='';maxlen:=0; for i:=1 to n do begin readln(ss[i]); if length(ss[i])>maxlen then maxlen:=length(ss[i]); end; maxlen:=1 << (trunc(ln(maxlen)/ln(2))+1); fillchar(pos,sizeof(pos),0); for i:=1 to n do begin l[i]:=length(s)+1; s:=s+ss[i]; r[i]:=length(s); for j:=l[i] to r[i] do pos[j-1]:=i; for j:=1 to maxlen do s:=s+chr(i); end; m:=length(s); for i:=0 to m-1 do a[i]:=ord(s[i+1]); Suffix_Array; Calc_Height; Lx:=1;Rx:=maxlen;ans:=0; while Lx<=Rx do begin mid:=(Lx+Rx) >> 1; if solve(mid) then begin ans:=mid;Lx:=mid+1; end else Rx:=mid-1; end; writeln(ans); end.
相关文章推荐
- [BZOJ2946][Poi2000]公共串解题报告|后缀自动机
- 【BZOJ 2946】[Poi2000]公共串 后缀数组
- BZOJ 2946 [Poi2000]公共串 后缀数组
- [BZOJ2946][Poi2000]公共串 && 后缀自动机
- BZOJ 2946: [Poi2000]公共串( 后缀自动机 )
- Bzoj2946:[POI2000] 最长公共子串
- SPOJ LCS2 Longest Common Substring II + BZOJ 2946 [Poi2000]公共串
- [BZOJ2946][Poi2000]公共串 后缀自动机
- bzoj 2946: [Poi2000]公共串 后缀自动机
- 2946: [Poi2000]公共串 后缀数组
- [BZOJ2946][Poi2000]公共串(后缀自动机)
- BZOJ2946: [Poi2000]公共串
- 2946: [Poi2000]公共串|哈希|后缀数组
- bzoj 2946: [Poi2000]公共串 后缀自动机
- 【bzoj2946】[Poi2000]公共串 后缀自动机
- 【BZOJ 2946】【POI 2000】公共串【后缀数组】【裸】
- bzoj 2946: [Poi2000]公共串 (后缀自动机)
- bzoj2946 [Poi2000]公共串
- BZOJ_2946_[Poi2000]公共串_后缀数组+二分答案
- [BZOJ2946][Poi2000]公共串