HDU 4622(字尾自動機)
阿新 • • 發佈:2018-12-09
傳送門
題面:
czh得意的向cry炫耀自己的英文水平,cry很不屑的扔給czh一篇文章,雖然czh看完之後頭暈眼花,但他還是決定猜測一下文章中單詞的意思,由於文章連空格都沒有,這大大增加了閱讀的難度,於是他決定退而求其次,只需要計算一下從第L個字元到第R個字元中有多少個可能的互不相同單詞(任意長度 >=1 ,任意組合的字元都是一個可能的單詞)就行了,這時候就需要聰明的你們來幫忙了。
Input
第一行讀入T,表示資料組數(T<=5)
對於每組資料,第一行讀入字串S len[S]<2000 為cry給出的文章
第二行一個正整數Q(Q<=10000)表示提問次數
接下來Q行,每行兩個正整數L,R
Output
對於每次提問,每行輸出一個答案
Sample Input
2
cchch
3
1 4
2 5
3 4
hzzhz
4
5 5
3 5
1 4
3 3
Sample Output
8
7
3
1
5
8
1
題意:
給你一個字串,給你q個詢問,問你在字串區間[l,r]中有多少個本質不同的字串。
題目分析:
字尾自動機模板題呀。
個人理解字尾自動機就是將一顆字尾樹的空間不斷壓縮而形成的一張有向圖。而建立出來的字尾自動機中的從root開始走的到結尾的所有不同的路徑均代表著一種本質不同的字串。其中有兩個很重要的數字,其中l[i]代表了從root走到第i個結點的距離,而fa指標則代表著第i個結點的字尾連線(即fa[i]是i的字尾)
而在我們加入每一個字元的過程中,加入該字元所增加的路徑為,因此根據定義,則此時整個串所增加的本質不同的迴文串的個數也為。
而對於這個題,因為詢問比較大(1e4)但字串str長度較小(1e2),因此我們可以考慮用字尾自動機預處理出來字串str的所有位置的字串的個數,之後o1查詢即可。
程式碼:
#include <bits/stdc++.h> #define maxn 2005 using namespace std; char str[maxn]; int len; int ans[maxn][maxn]; struct SAM{ int next[maxn][26],fa[maxn],l[maxn]; int last,id; int tot; int newnode(){ for(int i=0;i<26;i++) next[id][i]=0; fa[id]=l[id]=0; return id++; } void init(){ last=id=1; newnode(); tot=0; } int Insert(int c){ int p=last; int np=newnode(); l[np]=l[p]+1; last=np; while(p&&!next[p][c]) next[p][c]=np,p=fa[p]; if(!p) fa[np]=1; else{ int q=next[p][c]; if(l[q]==l[p]+1) fa[np]=q; else{ int nq=newnode(); memcpy(next[nq],next[q],sizeof(next[q])); l[nq]=l[p]+1; fa[nq]=fa[q]; fa[np]=fa[q]=nq; while(next[p][c]==q) next[p][c]=nq,p=fa[p]; } } tot+=l[last]-l[fa[last]]; return tot; } }sam; int main() { int t; scanf("%d",&t); while(t--){ scanf("%s",str); len=strlen(str); sam.init(); for(int i=0;i<len;i++){ sam.init(); for(int j=i;j<len;j++){ ans[i+1][j+1]=sam.Insert(str[j]-'a'); } } int m; scanf("%d",&m); while(m--){ int l,r; scanf("%d%d",&l,&r); cout<<ans[l][r]<<endl; } } return 0; }