后缀自动机模板题hdu_后缀自动机习题合集

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

constmaxn=400000;varson:array[0..maxn,0..25]oflongint;

pre,step:array[0..maxn]oflongint;

last,tot:longint;functionaddpoint:longint;vari:longint;begininc(tot);//pre[tot]:=-1;// for i:=0 to 25 do son[tot,i]:=0;

exit(tot);end;procedureadd(x:longint);vari,new,now,old,more:longint;beginnew:=addpoint;

now:=last;

step[new]:=step[now]+1;while (now>=0) and (son[now,x]=0) do beginson[now,x]:=new;

now:=pre[now];end;

last:=new;if now<0 then pre[new]:=0

else

if step[son[now,x]]=step[now]+1 thenpre[new]:=son[now,x]else beginold:=son[now,x];

more:=addpoint;for i:=0 to 25 do son[more,i]:=son[old,i];

step[more]:=step[now]+1;

pre[more]:=pre[old];

pre[old]:=more;

pre[new]:=more;while (now>=0) and (son[now,x]=old) do beginson[now,x]:=more;

now:=pre[now];end;end;end;procedurework;vars:ansistring;

now,max,long,i,j:longint;beginfillchar(pre,sizeof(pre),255);

readln(s);for i:=1 to length(s) do add(ord(s[i])-97);

readln(s);

now:=0;

max:=0;

long:=0;for i:=1 to length(s) do beginj:=ord(s[i])-97;if son[now,j]<>0 then begininc(long);

now:=son[now,j];end

else begin

while (now>=0) and (son[now,j]=0) do now:=pre[now];if now<0 then beginlong:=0;

now:=0;end

else beginlong:=step[now]+1;

now:=son[now,j];end;end;if long>max then max:=long;end;

writeln(max);end;beginwork;end.

View Code

SPOJ-1812 Longest Common Substring II

题意:求多个字符串的最长公共字串。

还是以第一个字符串建个sam。然后其他字符串像上一道一样跑sam。

那么状态s,其他串对它的匹配长度分别是a1,a2,a3……an的话,状态s的最长公共字符串就是min(a1,a2,a3……an,max(s))。

然后去最长的状态就行了!

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

constmaxn=200033;varp,num,step,ans,pre:array[0..maxn]oflongint;

son:array[0..maxn,0..25]oflongint;

tot,last:longint;functionmax(x,y:longint):longint;begin

if x

exit(x);end;functionmin(x,y:longint):longint;begin

if x

exit(y);end;procedureadd(x:longint);vari,now,new,more,old:longint;begininc(tot);

new:=tot;

now:=last;

last:=new;

step[new]:=step[now]+1;while (now>=0) and (son[now,x]=0) do beginson[now,x]:=new;

now:=pre[now];end;if now<0 then pre[new]:=0

else beginold:=son[now,x];if step[now]+1=step[old] then pre[new]:=oldelse begininc(tot);

more:=tot;for i:=0 to 25 do son[more,i]:=son[old,i];

step[more]:=step[now]+1;

pre[more]:=pre[old];

pre[old]:=more;

pre[new]:=more;while (now>=0) and (son[now,x]=old) do beginson[now,x]:=more;

now:=pre[now];end;end;end;end;procedureinto;vars:ansistring;

i,len:longint;begintot:=0;

pre[0]:=-1;

readln(s);

len:=length(s);for i:=1 to len doadd(ord(s[i])-97);

fillchar(num,sizeof(num),0);for i:=1 to tot doinc(num[step[i]]);for i:=1 to len do inc(num[i],num[i-1]);for i:=1 to tot do beginp[num[step[i]]]:=i;

dec(num[step[i]]);end;for i:=1 to tot do beginnum[i]:=0;

ans[i]:=step[p[i]];end;end;procedurework;vars:ansistring;

i,j,now,long,x,answer,n:longint;begin

while not eof do beginreadln(s);

now:=0;

long:=0;for i:=1 to length(s) do beginj:=ord(s[i])-97;if son[now,j]<>0 then beginnow:=son[now,j];

inc(long);

num[now]:=max(num[now],long);end

else begin

while (now>=0) and (son[now,j]=0) do now:=pre[now];if now<0 then beginnow:=0;

long:=0;end

else beginlong:=step[now]+1;

now:=son[now,j];

num[now]:=max(num[now],long);end;end;end;for i:=tot downto 1 do beginx:=p[i];

step[x]:=min(step[x],num[x]);

num[pre[x]]:=max(num[pre[x]],num[x]);

num[x]:=0;end;end;

answer:=0;for i:=1 to tot do

if answer

writeln(answer);end;begininto;

work;end.

View Code

SPOJ-8222 Substrings

题意:给一个字符串s,求长度为i(i属于【1,len】)的出现次数最多的子串出现的次数(好绕)……

上一篇有提到子串出现的个数就是right集合的大小。

那么用那个方法就行了(其实不用dfs序,可以用拓扑和桶排,拓扑要开多几个数组,桶排不用……速度没比过)

注意right树中要用儿子更新父亲,就是长度为i+1可以更新长度为i的值……

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

typearr=recordfrom,next:longint;end;constmaxn=600000;varedge:array[0..maxn]ofarr;

ans,step,first,sum,num,pre,p:array[0..maxn]oflongint;

son:array[0..maxn,0..25]oflongint;

tot,last,len,esum:longint;functionmax(x,y:longint):longint;begin

if x>y thenexit(x);

exit(y);end;functionaddpoint:longint;begininc(tot);

exit(tot);end;procedureaddedge(j,k:longint);begininc(esum);

edge[esum].from:=j;

edge[esum].next:=first[k];

first[k]:=esum;

inc(num[j]);end;procedureadd(x:longint);varnow,new,more,old,i:longint;beginnew:=addpoint;

now:=last;

step[new]:=step[now]+1;while (now>=0) and (son[now,x]=0) do beginson[now,x]:=new;

now:=pre[now];end;

last:=new;if now<0 then pre[now]:=0

else beginold:=son[now,x];if step[old]=step[now]+1 thenpre[new]:=oldelse beginmore:=addpoint;for i:=0 to 25 do son[more,i]:=son[old,i];

step[more]:=step[now]+1;

pre[more]:=pre[old];

pre[new]:=more;

pre[old]:=more;while (now>=0) and (son[now,x]=old) do beginson[now,x]:=more;

now:=pre[now];end;end;end;end;procedureinto;vars:ansistring;

i,j:longint;beginreadln(s);

pre[0]:=-1;

last:=0;

len:=length(s);for i:=1 to len do add(ord(s[i])-97);for i:=1 to tot do

for j:=0 to 25 do

if son[i,j]<>0 thenaddedge(i,son[i,j]);end;procedurework;varhead,tail,i,x,fa:longint;beginhead:=1;

tail:=0;while last>=0 do beginsum[last]:=1;

last:=pre[last];end;for i:=0 to tot do

if num[i]=0 then begininc(tail);

p[tail]:=i;end;while head<=tail do beginx:=p[head];

i:=first[x];while i>0 do beginfa:=edge[i].from;

inc(sum[fa],sum[x]);

dec(num[fa]);if num[fa]=0 then begininc(tail);

p[tail]:=fa;end;

i:=edge[i].next;end;

inc(head);end;for i:=1 to tot doans[step[i]]:=max(ans[step[i]],sum[i]);for i:=len-1 downto 1 doans[i]:=max(ans[i],ans[i+1]);for i:=1 to len dowriteln(ans[i]);end;begininto;

work;end.

View Code

SPOJ-7258 Lexicographical Substring Search

题意:给定一个字符串,取出所有的子串按照字典序排序并去重后,求第K大的子串。

先预处理出每个节点的所包含子串总数,然后对每个询问在sam上爬一爬就行了!

由于spoj卡得紧,处理询问的时候不能从0-25都扫一遍,要先记录儿子的数量,具体看代码吧。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

constmaxn=200000;varstep,num,sum,pre,p:array[0..maxn]oflongint;

col,too,son:array[0..maxn,0..25]oflongint;

last,tot:longint;procedureadd(x:longint);vari,new,now,more,old:longint;begininc(tot);

new:=tot;

now:=last;

step[new]:=step[now]+1;

last:=new;while (now>=0) and (son[now,x]=0)do beginson[now,x]:=new;

now:=pre[now];end;if now<0 then pre[new]:=0

else beginold:=son[now,x];if step[old]=step[now]+1 then pre[new]:=oldelse begininc(tot);

more:=tot;for i:=0 to 25 do son[more,i]:=son[old,i];

step[more]:=step[now]+1;

pre[more]:=pre[old];

pre[new]:=more;

pre[old]:=more;while (now>=0) and (son[now,x]=old) do beginson[now,x]:=more;

now:=pre[now];end;end;end;end;procedureinto;vars:ansistring;

i,j,x,len:longint;beginpre[0]:=-1;

last:=0;

tot:=0;

readln(s);

len:=length(s);for i:=1 to len do add(ord(s[i])-97);

fillchar(num,sizeof(num),0);for i:=1 to tot do beginsum[i]:=1;

inc(num[step[i]]);end;for i:=2 to len do inc(num[i],num[i-1]);for i:=1 to tot do beginp[num[step[i]]]:=i;

dec(num[step[i]]);end;

fillchar(num,sizeof(num),0);for i:=tot downto 0 do beginx:=p[i];for j:=0 to 25 do

if son[x,j]<>0 then begininc(num[x]);

col[x,num[x]]:=j;

too[x,num[x]]:=son[x,j];

inc(sum[x],sum[son[x,j]]);end;end;//for i:=0 to tot dowriteln(sum[i]);end;procedurework;varq,n,i,k,now:longint;

answer:ansistring;beginreadln(q);while q>0 do begindec(q);

readln(n);

answer:='';

now:=0;while n>0 do begin

for i:=1 to num[now] do begink:=too[now,i];if sum[k]

answer:=answer+chr(97+col[now,i]);

now:=k;

break;end;end;end;

writeln(answer);end;end;begininto;

workend.

View Code

HDU-4622 Reincarnation

题意:给定一个字符串,多组询问,每个询问给一个区间,求出该区间内共有多少个不同的子串。

如果是单个子串好处理&(sa和sam都可以嘛)。

但是现在要求区间。我们可以这样考虑,每次按左端点以此往右端点建。比如当前是【l,r】然后变成【l,r+1】的话,就是sam(【l,r】)变成sam(【l,r+1】)。多出的子串就是step【last】-step【pre【last】】(这个很重要,在统计时经常用到这个式子)

然后就直接处理出所有区间就行了……(暴力到不敢相信)

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

constmaxn=5000;varans:array[0..2100,0..2100]oflongint;

pre,step:array[0..maxn]oflongint;

son:array[0..maxn,0..25]oflongint;

tot,last,t,n,len,i,j:longint;

s:ansistring;functionaddpoint:longint;begininc(tot);

pre[tot]:=-1;

fillchar(son[tot],sizeof(son[tot]),0);

addpoint:=totend;procedureadd(x:longint);vari,now,new,old,more:longint;beginnew:=addpoint;

now:=last;

last:=new;

step[new]:=step[now]+1;while (now>=0) and (son[now,x]=0) do beginson[now,x]:=new;

now:=pre[now];end;if now<0 then pre[new]:=0

else beginold:=son[now,x];if step[old]=step[now]+1 then pre[new]:=oldelse beginmore:=addpoint;for i:=0 to 25 do son[more,i]:=son[old,i];

step[more]:=step[now]+1;

pre[more]:=pre[old];

pre[old]:=more;

pre[new]:=more;while (now>=0) and (son[now,x]=old) do beginson[now,x]:=more;

now:=pre[now];end;end;end

end;beginreadln(t);while t>0 do begindec(t);

readln(s);

len:=length(s);for i:=1 to len do begintot:=-1;

last:=addpoint;

ans[i,i-1]:=0;for j:=i to len do beginadd(ord(s[j])-97);

ans[i,j]:=ans[i,j-1]+step[last]-step[pre[last]];end;end;

readln(n);while n>0 do begindec(n);

readln(i,j);

writeln(ans[i,j]);end;end

end.

View Code

(当然也可以快排询问,我懒所以没有做)

HDU-4641 K-string

题意:给定字符串,有m次操作,每次操作可以向该字符串末尾添加一个字符或者询问在字符串中出现了至少K次的子串一共有多少个?

就是在插入字符后顺便统计一下就行了,如果这个点出现次数大于k那么一定已经在ans中了。

要注意的是在step【old】>step【now】+1时复制点时,出现次数要也一起复制过去……

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

constmaxn=500000;varstep,num,pre:array[0..maxn]oflongint;

son:array[0..maxn,0..25]oflongint;

ans,tot,last,n,m,kk,i,j:longint;

ch:char;

s:ansistring;functionaddpoint:longint;vari:longint;begininc(tot);

pre[tot]:=-1;

step[tot]:=0;

num[tot]:=0;for i:=0 to 25 doson[tot,i]:=0;

addpoint:=tot;end;procedureadd(x:longint);vari,now,new,more,old:longint;beginnew:=addpoint;

now:=last;

last:=new;

step[new]:=step[now]+1;while (now>=0) and (son[now,x]=0) do beginson[now,x]:=new;

now:=pre[now];end;if now<0 then pre[new]:=0

else beginold:=son[now,x];if step[old]=step[now]+1 then pre[new]:=oldelse beginmore:=addpoint;for i:=0 to 25 do son[more,i]:=son[old,i];

step[more]:=step[now]+1;

num[more]:=num[old];

pre[more]:=pre[old];

pre[old]:=more;

pre[new]:=more;while (now>=0) and (son[now,x]=old) do beginson[now,x]:=more;

now:=pre[now];end;end;end;

now:=last;while (now>0) and (num[now]

now:=pre[now];end;end;begin

while not eof do beginreadln(n,m,kk);

tot:=-1;

last:=addpoint;

ans:=0;

readln(s);for i:=1 to length(s) doadd(ord(s[i])-97);while m>0 do begindec(m);

read(j);if j=1 then beginread(ch);

read(ch);

add(ord(ch)-97);{for i:=0 to tot do begin

write(i,':');

for j:=0 to 25 do

write(son[i,j],' ');

writeln;

end;}

end

elsewriteln(ans);

readln;end;end

end.

View Code

待填之坑

SPOJ-8747. Substrings II && BZOJ-2555: SubString ( Suffix Automaton + Link Cut Tree )/BZOJ 2555 Substring (要lct吓傻)

BZOJ 3238 AHOI2013 差异 后缀自动机

BZOJ 2882 工艺 后缀自动机

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值