(寫的都是初中小朋友czl早就切過的題……)html
http://www.cnblogs.com/Lyush/p/3281546.html數組
題意:一個字符串能夠將第一個字符放到最後一位,而後問不斷這樣作能夠獲得的字典序最小的字符串ide
sam模板題,copy一遍建個sam,而後直接在sam中跑一遍就好了。學習
sam記錄了字符串的全部後綴(也隨便記錄了字串),從root開始到每一個接受態節點都是一個後綴(或多個),從root開始到每一個節點都是一個子串(或多個)。因爲copy一遍後把全部的狀況的包括進去了,那麼只要跑len(原長)遍就好了!spa
const maxn=30000; var step,pre:array[0..maxn]of longint; son:array[0..maxn,0..25]of longint; tot,tail,t:longint; procedure add(x:longint); var i,j,k,np,p:longint; begin p:=tail; inc(tot); np:=tot; step[np]:=step[tail]+1; while (p>=0) and (son[p,x]=-1) do begin son[p,x]:=np; p:=pre[p]; end; tail:=np; if p<0 then pre[np]:=0 else if step[son[p,x]]=step[p]+1 then pre[np]:=son[p,x] else begin j:=son[p,x]; inc(tot); k:=tot; for i:=0 to 25 do son[k,i]:=son[j,i]; step[k]:=step[p]+1; pre[k]:=pre[j]; pre[j]:=k; pre[np]:=k; while (p>=0) and (son[p,x]=j) do begin son[p,x]:=k; p:=pre[p]; end; end; end; procedure work; var s:ansistring; len,i,j,k,p:longint; begin readln(s); len:=length(s); s:=s+s; for i:=1 to len<<1 do add(ord(s[i])-97); p:=0; for i:=1 to len do for j:=0 to 25 do if son[p,j]>=0 then begin p:=son[p,j]; break; end; writeln(step[p]-len+1); end; begin readln(t); while t>0 do begin dec(t); fillchar(pre,sizeof(pre),255); fillchar(son,sizeof(son),255); fillchar(step,sizeof(step),0); tot:=0; tail:=0; work; end end.
題意:求兩個字符串的最長公共字串。code
(彷佛作sa的時候作過?而後翻出sa的代碼,交上去光榮tle……sam虐殺sa!!)htm
繼續貼麗潔姐論文話:blog
const maxn=400000; var son:array[0..maxn,0..25]of longint; pre,step:array[0..maxn]of longint; last,tot:longint; function addpoint:longint; var i:longint; begin inc(tot); //pre[tot]:=-1; // for i:=0 to 25 do son[tot,i]:=0; exit(tot); end; procedure add(x:longint); var i,new,now,old,more:longint; begin new:=addpoint; now:=last; step[new]:=step[now]+1; while (now>=0) and (son[now,x]=0) do begin son[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 then pre[new]:=son[now,x] else begin old:=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 begin son[now,x]:=more; now:=pre[now]; end; end; end; procedure work; var s:ansistring; now,max,long,i,j:longint; begin fillchar(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 begin j:=ord(s[i])-97; if son[now,j]<>0 then begin inc(long); now:=son[now,j]; end else begin while (now>=0) and (son[now,j]=0) do now:=pre[now]; if now<0 then begin long:=0; now:=0; end else begin long:=step[now]+1; now:=son[now,j]; end; end; if long>max then max:=long; end; writeln(max); end; begin work; end.
題意:求多個字符串的最長公共字串。排序
仍是以第一個字符串建個sam。而後其餘字符串像上一道同樣跑sam。字符串
那麼狀態s,其餘串對它的匹配長度分別是a1,a2,a3……an的話,狀態s的最長公共字符串就是min(a1,a2,a3……an,max(s))。
而後去最長的狀態就好了!
const maxn=200033; var p,num,step,ans,pre:array[0..maxn]of longint; son:array[0..maxn,0..25]of longint; tot,last:longint; function max(x,y:longint):longint; begin if x<y then exit(y); exit(x); end; function min(x,y:longint):longint; begin if x<y then exit(x); exit(y); end; procedure add(x:longint); var i,now,new,more,old:longint; begin inc(tot); new:=tot; now:=last; last:=new; step[new]:=step[now]+1; while (now>=0) and (son[now,x]=0) do begin son[now,x]:=new; now:=pre[now]; end; if now<0 then pre[new]:=0 else begin old:=son[now,x]; if step[now]+1=step[old] then pre[new]:=old else begin inc(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 begin son[now,x]:=more; now:=pre[now]; end; end; end; end; procedure into; var s:ansistring; i,len:longint; begin tot:=0; pre[0]:=-1; 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 inc(num[step[i]]); for i:=1 to len do inc(num[i],num[i-1]); for i:=1 to tot do begin p[num[step[i]]]:=i; dec(num[step[i]]); end; for i:=1 to tot do begin num[i]:=0; ans[i]:=step[p[i]]; end; end; procedure work; var s:ansistring; i,j,now,long,x,answer,n:longint; begin while not eof do begin readln(s); now:=0; long:=0; for i:=1 to length(s) do begin j:=ord(s[i])-97; if son[now,j]<>0 then begin now:=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 begin now:=0; long:=0; end else begin long:=step[now]+1; now:=son[now,j]; num[now]:=max(num[now],long); end; end; end; for i:=tot downto 1 do begin x:=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<step[i] then answer:=step[i]; writeln(answer); end; begin into; work; end.
題意:給一個字符串s,求長度爲i(i屬於【1,len】)的出現次數最多的子串出現的次數(好繞)……
上一篇有提到子串出現的個數就是right集合的大小。
那麼用那個方法就好了(其實不用dfs序,能夠用拓撲和桶排,拓撲要開多幾個數組,桶排不用……速度沒比過)
注意right樹中要用兒子更新父親,就是長度爲i+1能夠更新長度爲i的值……
type arr=record from,next:longint; end; const maxn=600000; var edge:array[0..maxn]of arr; ans,step,first,sum,num,pre,p:array[0..maxn]of longint; son:array[0..maxn,0..25]of longint; tot,last,len,esum:longint; function max(x,y:longint):longint; begin if x>y then exit(x); exit(y); end; function addpoint:longint; begin inc(tot); exit(tot); end; procedure addedge(j,k:longint); begin inc(esum); edge[esum].from:=j; edge[esum].next:=first[k]; first[k]:=esum; inc(num[j]); end; procedure add(x:longint); var now,new,more,old,i:longint; begin new:=addpoint; now:=last; step[new]:=step[now]+1; while (now>=0) and (son[now,x]=0) do begin son[now,x]:=new; now:=pre[now]; end; last:=new; if now<0 then pre[now]:=0 else begin old:=son[now,x]; if step[old]=step[now]+1 then pre[new]:=old else begin more:=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 begin son[now,x]:=more; now:=pre[now]; end; end; end; end; procedure into; var s:ansistring; i,j:longint; begin readln(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 then addedge(i,son[i,j]); end; procedure work; var head,tail,i,x,fa:longint; begin head:=1; tail:=0; while last>=0 do begin sum[last]:=1; last:=pre[last]; end; for i:=0 to tot do if num[i]=0 then begin inc(tail); p[tail]:=i; end; while head<=tail do begin x:=p[head]; i:=first[x]; while i>0 do begin fa:=edge[i].from; inc(sum[fa],sum[x]); dec(num[fa]); if num[fa]=0 then begin inc(tail); p[tail]:=fa; end; i:=edge[i].next; end; inc(head); end; for i:=1 to tot do ans[step[i]]:=max(ans[step[i]],sum[i]); for i:=len-1 downto 1 do ans[i]:=max(ans[i],ans[i+1]); for i:=1 to len do writeln(ans[i]); end; begin into; work; end.
題意:給定一個字符串,取出全部的子串按照字典序排序並去重後,求第K大的子串。
先預處理出每一個節點的所包含子串總數,而後對每一個詢問在sam上爬一爬就好了!
因爲spoj卡得緊,處理詢問的時候不能從0-25都掃一遍,要先記錄兒子的數量,具體看代碼吧。
const maxn=200000; var step,num,sum,pre,p:array[0..maxn]of longint; col,too,son:array[0..maxn,0..25]of longint; last,tot:longint; procedure add(x:longint); var i,new,now,more,old:longint; begin inc(tot); new:=tot; now:=last; step[new]:=step[now]+1; last:=new; while (now>=0) and (son[now,x]=0)do begin son[now,x]:=new; now:=pre[now]; end; if now<0 then pre[new]:=0 else begin old:=son[now,x]; if step[old]=step[now]+1 then pre[new]:=old else begin inc(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 begin son[now,x]:=more; now:=pre[now]; end; end; end; end; procedure into; var s:ansistring; i,j,x,len:longint; begin pre[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 begin sum[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 begin p[num[step[i]]]:=i; dec(num[step[i]]); end; fillchar(num,sizeof(num),0); for i:=tot downto 0 do begin x:=p[i]; for j:=0 to 25 do if son[x,j]<>0 then begin inc(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 do writeln(sum[i]); end; procedure work; var q,n,i,k,now:longint; answer:ansistring; begin readln(q); while q>0 do begin dec(q); readln(n); answer:=''; now:=0; while n>0 do begin for i:=1 to num[now] do begin k:=too[now,i]; if sum[k]<n then dec(n,sum[k]) else begin dec(n); answer:=answer+chr(97+col[now,i]); now:=k; break; end; end; end; writeln(answer); end; end; begin into; work end.
題意:給定一個字符串,多組詢問,每一個詢問給一個區間,求出該區間內共有多少個不一樣的子串。
若是是單個子串好處理&(sa和sam均可以嘛)。
可是如今要求區間。咱們能夠這樣考慮,每次按左端點以此往右端點建。好比當前是【l,r】而後變成【l,r+1】的話,就是sam(【l,r】)變成sam(【l,r+1】)。多出的子串就是step【last】-step【pre【last】】(這個很重要,在統計時常常用到這個式子)
而後就直接處理出全部區間就好了……(暴力到不敢相信)
const maxn=5000; var ans:array[0..2100,0..2100]of longint; pre,step:array[0..maxn]of longint; son:array[0..maxn,0..25]of longint; tot,last,t,n,len,i,j:longint; s:ansistring; function addpoint:longint; begin inc(tot); pre[tot]:=-1; fillchar(son[tot],sizeof(son[tot]),0); addpoint:=tot end; procedure add(x:longint); var i,now,new,old,more:longint; begin new:=addpoint; now:=last; last:=new; step[new]:=step[now]+1; while (now>=0) and (son[now,x]=0) do begin son[now,x]:=new; now:=pre[now]; end; if now<0 then pre[new]:=0 else begin old:=son[now,x]; if step[old]=step[now]+1 then pre[new]:=old else begin 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 begin son[now,x]:=more; now:=pre[now]; end; end; end end; begin readln(t); while t>0 do begin dec(t); readln(s); len:=length(s); for i:=1 to len do begin tot:=-1; last:=addpoint; ans[i,i-1]:=0; for j:=i to len do begin add(ord(s[j])-97); ans[i,j]:=ans[i,j-1]+step[last]-step[pre[last]]; end; end; readln(n); while n>0 do begin dec(n); readln(i,j); writeln(ans[i,j]); end; end end.
(固然也能夠快排詢問,我懶因此沒有作)
題意:給定字符串,有m次操做,每次操做能夠向該字符串末尾添加一個字符或者詢問在字符串中出現了至少K次的子串一共有多少個?
就是在插入字符後順便統計一下就好了,若是這個點出現次數大於k那麼必定已經在ans中了。
要注意的是在step【old】>step【now】+1時複製點時,出現次數要也一塊兒複製過去……
const maxn=500000; var step,num,pre:array[0..maxn]of longint; son:array[0..maxn,0..25]of longint; ans,tot,last,n,m,kk,i,j:longint; ch:char; s:ansistring; function addpoint:longint; var i:longint; begin inc(tot); pre[tot]:=-1; step[tot]:=0; num[tot]:=0; for i:=0 to 25 do son[tot,i]:=0; addpoint:=tot; end; procedure add(x:longint); var i,now,new,more,old:longint; begin new:=addpoint; now:=last; last:=new; step[new]:=step[now]+1; while (now>=0) and (son[now,x]=0) do begin son[now,x]:=new; now:=pre[now]; end; if now<0 then pre[new]:=0 else begin old:=son[now,x]; if step[old]=step[now]+1 then pre[new]:=old else begin more:=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 begin son[now,x]:=more; now:=pre[now]; end; end; end; now:=last; while (now>0) and (num[now]<kk) do begin inc(num[now]); if num[now]=kk then ans:=ans+step[now]-step[pre[now]]; now:=pre[now]; end; end; begin while not eof do begin readln(n,m,kk); tot:=-1; last:=addpoint; ans:=0; readln(s); for i:=1 to length(s) do add(ord(s[i])-97); while m>0 do begin dec(m); read(j); if j=1 then begin read(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 else writeln(ans); readln; end; end end.
待填之坑
BZOJ 3238 AHOI2013 差別 後綴自動機
BZOJ 2882 工藝 後綴自動機
2015.04.14……看看人家學習的深度……唉,仍是太弱了 http://www.cnblogs.com/zyfzyf/p/4397216.html#3161819