哎,oi生涯也有一年了,竟然不知道后缀数组这种牛B的东西,啊~~~
可能是受到了后缀树的影响(一直分不清后缀树和后缀数组),对于串的理解一直停留在KMP和扩展KMP上,现参读了罗穗骞牛的《后缀数组——处理字符串的有力工具》,狠下心来研究了一番,发现串竟然是一个如此美妙的结构。
简要的说一下自己对后缀数组的理解:
定义suffix(i)为主串A1~length(A)的子串Ai~length(A)。
我们现在对所有后缀suffix(1),suffix(2),suffix(3),......,suffix(length(A)),按字典序从小到大排序,定义sa(i)为第i小的后缀,那么此时所有后缀之间将会有很漂亮的性质,比如和sa(i)公共前缀最长的后缀将会是sa(i-1)与sa(i+1)之中的一个,关于这个性质,我们又可以继续扩展它的功能,比如求任意两个后缀的公共前缀,通过转化成RMQ可以做到NlogN的复杂度。
一般地,对于一个字符串S,和S中第k个字符,定义子串T=S(i..j)为一个关于k的识别子串,当且仅当
1、i<=k<=j。
2、T在S中只出现一次。
your task
给出串S,求出所有关于i的识别子串。
利用后缀数组,我们可以求出每一个后缀的最长公共前缀为多少,换句话说,我们可以知道以i开头的子串只在主串中出现一次,最短可以为多少,由此便转化成了区间覆盖问题。
看了看考场上很多人的程序,都是非常不怕麻烦的写了线段树,问了佘兄发现还可以用单调队列优化,因为这道题满足如下性质:
1.对于我新插入的区间,如果有长度比我长的区间一定无用的;
2.对于已经从表头退出的区间,我们只需要记一个左端点的最大值即可。
关于后缀数组的实现,还是用倍增法比较好(加了优化的倍增法基本上接近O(N),具体可以参见罗穗骞2009年的论文)。
第一个后缀数组,代码蒯上来,纪念一下:
program ex2;type arr=array[0..100005] of longint; var a:ansistring; r,sa,y,c,h,x,q:arr; max,len,i,p,l,beg,clo,ans:longint; procedure sort(var a:arr;b:arr); begin fillchar(c,sizeof(c),0); for i:=1 to len do inc(c[a[i]+1]); for i:=1 to max do inc(c[i],c[i-1]); for i:=1 to len do begin inc(c[a[b[i]]]);sa[c[a[b[i]]]]:=b[i]; end; end; begin assign(input,'istring.in');reset(input); assign(output,'istring.out');rewrite(output); readln(a); len:=length(a); for i:=1 to len do r[i]:=ord(a[i])-96; for i:=1 to len do sa[i]:=i; l:=1; max:=26+ord(26=len); while max<>len do begin for i:=1 to len do if i+l<=len then y[i]:=r[i+l] else y[i]:=0; x:=r; sort(y,sa);sort(x,sa); max:=0;l:=l*2; for i:=1 to len do begin if (i=1) or (y[sa[i]]<>y[sa[i-1]]) or (x[sa[i]]<>x[sa[i-1]]) then inc(max); r[sa[i]]:=max; end; end; l:=0; for i:=1 to len do begin dec(l,ord(l>0)); if r[i]=1 then continue; p:=sa[r[i]-1]; while (i+l<=len) and (p+l<=len) and (a[i+l]=a[p+l]) do inc(l); h[r[i]]:=l; end; for i:=1 to len do if h[r[i]]>h[r[i]+1] then x[i]:=h[r[i]] else x[i]:=h[r[i]+1]; beg:=1;l:=-len; for i:=1 to len do begin if x[i]+i<=len then begin while (beg<=clo)and(x[q[clo]]>=x[i]) do dec(clo); inc(clo);q[clo]:=i; end; while (beg<=clo)and(x[q[beg]]+q[beg]<i) do begin if q[beg]>l then l:=q[beg]; inc(beg); end; ans:=i-l+1; if (beg<=clo)and(x[q[beg]]<ans) then ans:=x[q[beg]]+1; writeln(ans); end; close(input);close(output); end.