【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.
时间: 2024-12-08 20:50:58

【BZOJ3238】差异(后缀数组,单调栈)的相关文章

【bzoj3238】[Ahoi2013]差异 后缀数组+单调栈

题目描述 输入 一行,一个字符串S 输出 一行,一个整数,表示所求值 样例输入 cacao 样例输出 54 题解 后缀数组+单调栈,几乎同 bzoj3879 的后半部分. 我明显是做题做反了... 这里还是说一下这道题的做法. 先用后缀数组求出height. 然后由于有LCP(a,c)=min(LCP(a,b),LCP(b,c))(rank[a]<rank[b]<rank[c]),所以我们只需要知道排名相邻的两个后缀的LCP,而这就是height数组的定义. 转化为子问题:给出n个数,求所有子

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

bzoj 3238 [Ahoi2013]差异 后缀数组 + 单调栈

题目链接 Description 一个长度为\(n\)的字符串\(S\),令\(T_i\)表示它从第\(i\)个字符开始的后缀.求\[\sum_{1\leq i\leq j\leq n}len(T_i)+len(T_j)-2*lcp(T_i,T_j)\]其中,\(len(a)\)表示字符串\(a\)的长度,\(lcp(a,b)\)表示字符串\(a\)和字符串\(b\)的最长公共前缀. \(2\leq n\leq 500000\) 思路 \(O(n^2)\)枚举显然是不可行的,应从 贡献 的角度取

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

[bzoj3238][Ahoi2013]差异 Description Input 一行,一个字符串S Output 一行,一个整数,表示所求值 Sample Input cacao Sample Output 54 题解: 任意两个字符串的lcp是什么,就是如 a,b  那么若a==b 那么为len(a) 否则设sa[a]<sa[b] 那么为min(height[sa[a]+1-------sa[b]]) 1 #include<cstring> 2 #include<iostrea

HUID 5558 Alice&#39;s Classified Message 后缀数组+单调栈+二分

http://acm.hdu.edu.cn/showproblem.php?pid=5558 对于每个后缀suffix(i),想要在前面i - 1个suffix中找到一个pos,使得LCP最大.这样做O(n^2) 考虑到对于每一个suffix(i),最长的LCP肯定在和他排名相近的地方取得. 按排名大小顺序枚举位置,按位置维护一个递增的单调栈,对于每一个进栈的元素,要算一算栈内元素和他的LCP最大是多少. 如果不需要输出最小的下标,最大的直接是LCP(suffix(st[top]),  suff

[HAOI2016]找相同字符(后缀数组+单调栈)

[HAOI2016]找相同字符(后缀数组+单调栈) 题面 给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 分析 我们把两个字符串接在一起,中间加一个分隔符.如\(\text{AABB}\)和\(\text{BBAA}\)变成\(\text{AABB|BBAA}\).我们考虑两个相同字串,如\(\text{BB}\),它在新串中对应了两个后缀\(BB|BBAA\)和\(\text{BBAA}\)的LCP. 容易发现,LC

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

显然我们可以先把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)对后缀,所以我们

POJ 3415 Common Substrings(后缀数组+单调栈)

[题目链接] http://poj.org/problem?id=3415 [题目大意] 求出两个字符串长度大于k的公共子串的数目. [题解] 首先,很容易想到O(n2)的算法,将A串和B串加拼接符相连, 做一遍后缀数组,把分别属于A和B的所有后缀匹配,LCP-k+1就是对答案的贡献, 但是在这个基础上该如何优化呢. 我们可以发现按照sa的顺序下来,每个后缀和前面的串的LCP就是区间LCP的最小值, 那么我们维护一个单调栈,将所有单调递减的LCP值合并, 保存数量和长度,对每个属于B串的后缀更新

POJ 3415:后缀数组+单调栈优化

题意很简单,求两个字符串长度大于等于K的子串个数 一开始还是只会暴力..发现n^2根本没法做...看了题解理解了半天才弄出来,太弱了... 思路:把两个字符串连接后做一次后缀数组,求出height 暴力的想法自然是枚举第一个子串的起始位置i和第二个子串的起始位置j,肯定会T的 看了题解才知道有单调栈这种有优化方法.. 将后缀分为A组(起始点为第一个字符串).B组 设符合要求的lcp长度为a,则其对答案的贡献为a-k+1(长度为k~a的都是符合要求的) 一开始这里我也是有疑问的,比如说k=1,aa

POJ 3415 后缀数组+单调栈

题目大意: 给定A,B两种字符串,问他们当中的长度大于k的公共子串的个数有多少个 这道题目本身理解不难,将两个字符串合并后求出它的后缀数组 然后利用后缀数组求解答案 这里一开始看题解说要用栈的思想,觉得很麻烦就不做了,后来在比赛中又遇到就后悔了,到今天看了很久才算看懂 首先建一个栈,从栈底到栈顶都保证是单调递增的 我们用一个tot记录当前栈中所有项和一个刚进入的子串匹配所能得到的总的子串的数目(当然前提是,当前进入的子串height值比栈顶还大,那么和栈中任意一个子串匹配都保持当前栈中记录的那时