後綴自動機習題合集

(寫的都是初中小朋友czl早就切過的題……)html

http://www.cnblogs.com/Lyush/p/3281546.html數組

 

POJ-1509 Glass Beads

UVA - 719 Glass Beads

題意:一個字符串能夠將第一個字符放到最後一位,而後問不斷這樣作能夠獲得的字典序最小的字符串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.
View Code

 

SPOJ-1811 Longest Common Substring

題意:求兩個字符串的最長公共字串。code

(彷佛作sa的時候作過?而後翻出sa的代碼,交上去光榮tle……sam虐殺sa!!)htm

繼續貼麗潔姐論文話:blog

給兩個長度小於100000的字符串A和B,求出他們的最長公共連續子串。
咱們構造出A的後綴自動機SAM
對於SAM的每一個狀態s,令r爲Right(s)中任意的一個元素,它表明的是結束位置在r的,長度在[Min(s),Max(s)]之間的全部子串。
咱們不妨對狀態s,求出全部B的子串中,從任意r開始往前能匹配的最大長度L,那麼min(L,Max(s))就能夠更新答案了。
 咱們考慮用 SAM讀入字符串 B
令當前狀態爲 s,同時最大匹配長度爲 len
咱們讀入字符 x。若是 s有標號爲 x的邊,
那麼 s=trans(s,x),len = len+1
不然咱們找到 s的第一個祖先 a,它有標號爲 x的邊,令
s=trans(a,x),len=Max(a)+1
若是沒有這樣的祖先,那麼令 s=root,len=0
在過程當中更新狀態的最大匹配長度(在這道題中我沒有理解這句話,直接像之前處理kmp同樣邊跑邊更新,後來寫多個串的最長公共子串時就由於這個wa了很久!)
注意到咱們求的是對於任意一個 Right集合中的 r,最大的匹配長度。那麼對於一個狀態 s,它的結果天然也能夠做爲它 Parent的結果,咱們能夠從底到上更新一遍。
而後問題就解決了。
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.
View Code

 

SPOJ-1812 Longest Common Substring II

題意:求多個字符串的最長公共字串。排序

仍是以第一個字符串建個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.
View Code

 

 

SPOJ-8222 Substrings

題意:給一個字符串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.
View Code

 

SPOJ-7258 Lexicographical Substring Search

題意:給定一個字符串,取出全部的子串按照字典序排序並去重後,求第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.
View Code

 

HDU-4622 Reincarnation

題意:給定一個字符串,多組詢問,每一個詢問給一個區間,求出該區間內共有多少個不一樣的子串。

若是是單個子串好處理&(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.
View Code

(固然也能夠快排詢問,我懶因此沒有作)

 

HDU-4641 K-string

題意:給定字符串,有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.
View Code

 

待填之坑

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

BZOJ 3238 AHOI2013 差別 後綴自動機

BZOJ 2882 工藝 後綴自動機

 

2015.04.14……看看人家學習的深度……唉,仍是太弱了 http://www.cnblogs.com/zyfzyf/p/4397216.html#3161819

相關文章
相關標籤/搜索