首页 > 代码库 > 【BZOJ3238】差异(后缀数组,单调栈)

【BZOJ3238】差异(后缀数组,单调栈)

题意:

技术分享

思路:显然len(t[i])+len(t[j])这部分的和是一定的

那么问题就在于如何快速求出两两之间lcp之和

考虑将它们排名后用SA可以很方便的求出lcp,且对答案没有影响,因为形式都是数对

所以用SA求出height

每个位置的height作为lcp的区间为扩展到最左最右,直到height[x]<height[i],height[y]<height[i]

这样合法的左区间为[x+1,i],右区间为[i,y-1]

考虑如何维护一个支持寻找最靠近当前位置的比当前位置上的数小的位置

这个可以用两次单调栈维护

但还要考虑到重复计算的情况,比如有多个最小值覆盖了全部区域

我们可以在比较时其中一次取等号,一次不取

  1 var x,y,sa,rank,height,wc,wd,a:array[0..600000]of longint;
  2     l,r:array[1..600000]of int64;
  3     stk:array[1..600000]of longint;
  4     n,i,m,top:longint;
  5     ans,j:int64;
  6     ch:ansistring;
  7 
  8 function min(x,y:longint):longint;
  9 begin
 10  if x<y then exit(x);
 11  exit(y);
 12 end;
 13 
 14 function cmp(a,b,l:longint):boolean;
 15 begin
 16  exit((y[a]=y[b])and(y[a+l]=y[b+l]));
 17 end;
 18 
 19 procedure swap(var x,y:longint);
 20 var t:longint;
 21 begin
 22  t:=x; x:=y; y:=t;
 23 end;
 24 
 25 procedure getsa(n:longint);
 26 var i,j,p:longint;
 27 begin
 28  for i:=0 to n-1 do
 29  begin
 30   x[i]:=a[i];
 31   inc(wc[x[i]]);
 32  end;
 33  for i:=1 to m-1 do wc[i]:=wc[i-1]+wc[i];
 34  for i:=n-1 downto 0 do
 35  begin
 36   dec(wc[x[i]]);
 37   sa[wc[x[i]]]:=i;
 38  end;
 39  j:=1; p:=1;
 40  while p<n do
 41  begin
 42   p:=0;
 43   for i:=n-j to n-1 do
 44   begin
 45    y[p]:=i; inc(p);
 46   end;
 47   for i:=0 to n-1 do
 48    if sa[i]>=j then begin y[p]:=sa[i]-j; inc(p); end;
 49   for i:=0 to n-1 do wd[i]:=x[y[i]];
 50   for i:=0 to m-1 do wc[i]:=0;
 51   for i:=0 to n-1 do inc(wc[wd[i]]);
 52   for i:=1 to m-1 do wc[i]:=wc[i-1]+wc[i];
 53   for i:=n-1 downto 0 do
 54   begin
 55    dec(wc[wd[i]]);
 56    sa[wc[wd[i]]]:=y[i];
 57   end;
 58   for i:=0 to n do swap(x[i],y[i]);
 59   p:=1; x[sa[0]]:=0;
 60   for i:=1 to n-1 do
 61    if cmp(sa[i-1],sa[i],j) then x[sa[i]]:=p-1
 62     else begin x[sa[i]]:=p; inc(p); end;
 63   j:=j<<1;
 64   m:=p;
 65  end;
 66 end;
 67 
 68 procedure getheight(n:longint);
 69 var i,j,k:longint;
 70 begin
 71  k:=0;
 72  for i:=1 to n do rank[sa[i]]:=i;
 73  for i:=0 to n-1 do
 74  begin
 75   if k>0 then dec(k);
 76   j:=sa[rank[i]-1];
 77   while a[i+k]=a[j+k] do inc(k);
 78   height[rank[i]]:=k;
 79  end;
 80 end;
 81 
 82 begin
 83  assign(input,bzoj3238.in); reset(input);
 84  assign(output,bzoj3238.out); rewrite(output);
 85  readln(ch);
 86  n:=length(ch);
 87  for i:=0 to n-1 do a[i]:=ord(ch[i+1])-ord(a)+1;
 88  a[n]:=0; m:=300;
 89  getsa(n+1);
 90  getheight(n);
 91  stk[1]:=1; height[1]:=-maxlongint; top:=1;
 92  for i:=2 to n do
 93  begin
 94   while (top>0)and(height[i]<height[stk[top]]) do dec(top);
 95   if stk[top]=1 then l[i]:=2
 96    else l[i]:=stk[top]+1;
 97   inc(top); stk[top]:=i;
 98  end;
 99 
100  stk[1]:=n+1; height[n+1]:=-maxlongint; top:=1;
101  for i:=n downto 2 do
102  begin
103   while (top>0)and(height[i]<=height[stk[top]]) do dec(top);
104   if stk[top]=n+1 then r[i]:=n
105    else r[i]:=stk[top]-1;
106   inc(top); stk[top]:=i;
107  end;
108 
109 
110  ans:=int64(n-1)*int64(n)*(n+1) div 2;
111  for i:=2 to n do ans:=ans-2*(-l[i]+i+1)*(r[i]-i+1)*height[i];
112  //for i:=2 to n+1 do writeln(l[i], ,r[i]);
113  writeln(ans);
114  close(input);
115  close(output);
116 end.

 

【BZOJ3238】差异(后缀数组,单调栈)