首页 > 代码库 > BZOJ4566[HAOI2016]找相同字符

BZOJ4566[HAOI2016]找相同字符

Description

给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数。两个方案不同当且仅当这两
个子串中有一个位置不同。

Input

两行,两个字符串s1,s2,长度分别为n1,n2。1 <=n1, n2<= 200000,字符串中只有小写字母

Output

输出一个整数表示答案

Sample Input

aabb
bbaa

Sample Output

10

 

题解:

将两个字符串共同建出一个SAM(即先用A串建出SAM,再将last指为root,用B串继续建)。

这样,我们只用知道每个节点在A串中对应几个串、在B串中对应几个串(即在A、B中的size),在每次遍历到时相乘加到答案上。

求size的方法是将A、B串分别输入到自动机中,得到初始size1、size2,然后分别在pre树中dfs求出最终size1、size2。

访问节点i对答案的贡献为size1[i]*size2[i],可以记忆化搜索统计答案,或利用某个性质(升级版:只有长度大于等于k的子串才被统计)。

 

代码:

技术分享
var
  i,j,k,l,n,m,now,last,t,cnt:longint;
  ans:int64;
  a:array[0..800005,a..z]of longint;
  pre,step,c:array[0..800005]of longint;
  size1,size2:array[0..800005]of int64;
  b:array[0..800005,1..2]of longint;
  s1,s2:ansistring;
  ch:char;
function max(x,y:longint):longint;
begin if x>y then exit(x); exit(y); end;
procedure ss1(x,y:longint);
begin
  inc(size1[x]);
  if y>=length(s1)then exit;
  ss1(a[x,s1[y+1]],y+1);
end;
procedure ss2(x,y:longint);
begin
  inc(size2[x]);
  if y>=length(s2)then exit;
  ss2(a[x,s2[y+1]],y+1);
end;
procedure ss3(x:longint);
var i:longint;
begin
  i:=c[x];
  while i>0 do
  begin
    ss3(b[i,1]);
    size1[x]:=size1[x]+size1[b[i,1]];
    size2[x]:=size2[x]+size2[b[i,1]];
    i:=b[i,2];
  end;
end;
begin
  readln(s1); readln(s2);
  pre[0]:=-1; m:=0; last:=0;
  for i:=1 to length(s1) do
  begin
    inc(m); now:=m; step[now]:=step[last]+1;
    while(last<>-1)and(a[last,s1[i]]=0)do
    begin a[last,s1[i]]:=now; last:=pre[last]; end;
    if last=-1 then begin pre[now]:=0; last:=now; continue; end;
    if step[last]+1=step[a[last,s1[i]]] then
    begin pre[now]:=a[last,s1[i]]; last:=now; continue; end;
    j:=a[last,s1[i]];
    inc(m); a[m]:=a[j]; pre[m]:=pre[j]; step[m]:=step[last]+1;
    pre[j]:=m; pre[now]:=m;
    while last<>-1 do
    begin
      if a[last,s1[i]]=j then a[last,s1[i]]:=m else break;
      last:=pre[last];
    end;
    last:=now;
  end;
  last:=0;
  for i:=1 to length(s2) do
  begin
    inc(m); now:=m; step[now]:=step[last]+1;
    while(last<>-1)and(a[last,s2[i]]=0)do
    begin a[last,s2[i]]:=now; last:=pre[last]; end;
    if last=-1 then begin pre[now]:=0; last:=now; continue; end;
    if step[last]+1=step[a[last,s2[i]]] then
    begin pre[now]:=a[last,s2[i]]; last:=now; continue; end;
    j:=a[last,s2[i]];
    inc(m); a[m]:=a[j]; pre[m]:=pre[j]; step[m]:=step[last]+1;
    pre[j]:=m; pre[now]:=m;
    while last<>-1 do
    begin
      if a[last,s2[i]]=j then a[last,s2[i]]:=m else break;
      last:=pre[last];
    end;
    last:=now;
  end;
  cnt:=0;
  for i:=1 to m do
  begin inc(cnt); b[cnt,1]:=i; b[cnt,2]:=c[pre[i]]; c[pre[i]]:=cnt; end;
  ss1(0,0); ss2(0,0); ss3(0); ans:=0;
  for i:=1 to m do
  ans:=ans+(size1[i]*size2[i])*(step[i]-max(k,step[pre[i]]+1)+1);
  writeln(ans);
end.
View Code

BZOJ4566[HAOI2016]找相同字符