码迷,mamicode.com
首页 > 编程语言 > 详细

[bzoj3238]差异(后缀数组+单调栈)

时间:2016-04-04 01:29:08      阅读:331      评论:0      收藏:0      [点我收藏+]

标签:

技术分享

      显然我们可以先把len(Ti)+len(Tj)的值先算出来,再把LCP减去。所有len(Ti)+len(Tj)的值为n*(n-1)*(n+1)/2,这个随便在纸上画一画就可以算出来的。

      接下来问题就是如何把LCP减去。我们先用后缀数组把height求出来,当有一段区间l~r,height[i]为height[l]~height[r]中的最小值,那么在l~r当中随便取两个后缀,他们的LCP则都是height[i],这个很好理解吧。那么l~r这个区间里有(l-i+1)*(r-i+1)对后缀,所以我们最后的答案就要减去2*height[i]*(l-i+1)*(r-i+1)【1≤i≤n】。

      然后就是如何求出每一个i的l~r了,暴力枚举+RMQ显然不行,那我们就用一个单调栈,栈里存着i前面height值比height[i]小的height值的编号,记为j,如果height[j]比height[i]大那么就弹出,那么这段区间的左端点则为栈顶的j+1,右端点同理。这样我们就可以求出每个height的l和r了。

奇丑无比的代码如下:

技术分享
var
  s:ansistring;
  i:longint;
  n,m,l,r,ans,top:int64;
  rk,trk,sa,tsa,sum,h,ll,rr,st:array[0..500005]of int64;

procedure suffix;
var
  i,j,p:longint;
begin
  for i:=1 to n do begin trk[i]:=ord(s[i]);inc(sum[trk[i]]);end;
  for i:=2 to 255 do inc(sum[i],sum[i-1]);
  for i:=n downto 1 do begin sa[sum[trk[i]]]:=i;dec(sum[trk[i]]);end;
  rk[sa[1]]:=1;p:=1;
  for i:=2 to n do begin if trk[sa[i]]<>trk[sa[i-1]] then inc(p);rk[sa[i]]:=p;end;
  m:=p;j:=1;
  while m<n do
  begin
    move(rk,trk,sizeof(rk));fillchar(sum,sizeof(sum),0);p:=0;
    for i:=n-j+1 to n do begin inc(p);tsa[p]:=i;end;
    for i:=1 to n do if sa[i]>j then begin inc(p);tsa[p]:=sa[i]-j;end;
    for i:=1 to n do begin rk[i]:=trk[tsa[i]];inc(sum[rk[i]]);end;
    for i:=2 to n do inc(sum[i],sum[i-1]);
    for i:=n downto 1 do begin sa[sum[rk[i]]]:=tsa[i];dec(sum[rk[i]]);end;
    rk[sa[1]]:=1;p:=1;
    for i:=2 to n do
    begin
      if (trk[sa[i]]<>trk[sa[i-1]])or(trk[sa[i]+j]<>trk[sa[i-1]+j])then inc(p);
      rk[sa[i]]:=p;
    end;
    m:=p;j:=j*2;
  end;
  h[1]:=0;p:=0;
  for i:=1 to n do
  begin
    if rk[i]=1 then continue;
    j:=sa[rk[i]-1];
    while s[i+p]=s[j+p] do inc(p);
    h[rk[i]]:=p;
    if p>0 then dec(p);
  end;
end;

begin
  readln(s);
  n:=length(s);
  s:=s+ ;
  suffix;
  ans:=n*(n-1)*(n+1)div 2;
  h[0]:=-maxlongint;
  for i:=1 to n do
  begin
    while h[i]<=h[st[top]] do dec(top);
    if st[top]=0 then ll[i]:=1
    else ll[i]:=st[top]+1;
    inc(top);
    st[top]:=i;
  end;
  h[n+1]:=-maxlongint;top:=0;st[0]:=n+1;
  for i:=n downto 0 do
  begin
    while h[i]<h[st[top]] do dec(top);
    if st[top]=n+1 then rr[i]:=n
    else rr[i]:=st[top]-1;
    inc(top);
    st[top]:=i;
  end;
  for i:=1 to n do
  ans:=ans-2*(i-ll[i]+1)*(rr[i]-i+1)*h[i];
  writeln(ans);
end.
View Code

 

[bzoj3238]差异(后缀数组+单调栈)

标签:

原文地址:http://www.cnblogs.com/Sakits/p/5351175.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!