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))。
然后去最长的状态就行了!
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的值……
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都扫一遍,要先记录儿子的数量,具体看代码吧。
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】】(这个很重要,在统计时经常用到这个式子)
然后就直接处理出所有区间就行了……(暴力到不敢相信)
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时复制点时,出现次数要也一起复制过去……
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 工艺 后缀自动机