您的位置:首页 > 其它

【后缀数组+单调队列】省队集训题istring

2010-07-30 01:55 316 查看
哎,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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: