標籤:bzoj oi dp ac自動機
1030: [JSOI2007]文本產生器Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 2017 Solved: 834
[Submit][Status]Description
JSOI交給隊員ZYX一個任務,編製一個稱之為“文本產生器”的電腦軟體:該軟體的使用者是一些低幼人群,他們現在使用的是GW文本產生器v6版。該軟體可以隨機產生一些文章―――總是產生一篇長度固定且完全隨機的文章—— 也就是說,產生的文章中每個位元組都是完全隨機的。如果一篇文章中至少包含使用者們瞭解的一個單詞,那麼我們說這篇文章是可讀的(我們稱文章a包含單詞b,若且唯若單詞b是文章a的子串)。但是,即使按照這樣的標準,使用者現在使用的GW文本產生器v6版所產生的文章也是幾乎完全不可讀的。 ZYX需要指出GW文本產生器 v6產生的所有文本中可讀文本的數量,以便能夠成功獲得v7更新版。你能協助他嗎?
Input
輸入檔案的第一行包含兩個正整數,分別是使用者瞭解的單詞總數N (<= 60),GW文本產生器 v6產生的文本固定長度M;以下N行,每一行包含一個使用者瞭解的單詞。 這裡所有單詞及文本的長度不會超過100,並且只可能包含英文大寫字母A..Z 。
Output
一個整數,表示可能的文章總數。只需要知道結果模10007的值。
Sample Input2 2
A
BSample Output100
AC自動機+dp。
首先把所求轉化成 26^m減去一個單詞都不包含的方案數。
接下來把單詞插到AC自動機上進行dp。
f[i][j]表示走到AC自動機的j號結點當前單詞長度為i的方案數。
轉移就是枚舉下一位的26個字母,看是否可行(如果下一位在j的子節點中出現,若被標記為單詞的結尾,就不能轉移)
注意對於長度為i的單詞,枚舉AC自動機上的結點標號j,如果f[i][j]=0,說明長度為i-1的單詞沒有能轉移到j上的,因此f[i][j]自然也不能轉移到i+1。
#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>#include <queue>#include <cstdlib>#define mod 10007using namespace std;char s[100];queue<int> q;int n,m,tot,ans,f[105][10005];struct AC{int son[30],ne,last;bool mark;}tree[10005];void Insert(char str[]){int l=strlen(str),x=0;for (int i=0;i<l;i++){int y=str[i]-'A';if (!tree[x].son[y])tree[x].son[y]=++tot;x=tree[x].son[y];}tree[x].mark=true;}void Make_fail(){for (int i=0;i<26;i++)if (tree[0].son[i])q.push(tree[0].son[i]);while (!q.empty()){int x=q.front();q.pop();for (int i=0;i<26;i++)if (tree[x].son[i]){int y=tree[x].son[i];q.push(y);int v=tree[x].ne;while (v&&!tree[v].son[i])v=tree[v].ne;tree[y].ne=v=tree[v].son[i];tree[y].last=tree[v].mark?v:tree[v].last;}}}void dp(){f[0][0]=1;for (int i=0;i<m;i++)for (int j=0;j<=tot;j++)if (f[i][j])for (int k=0;k<26;k++){int v=j;while (v&&!tree[v].son[k])v=tree[v].ne;v=tree[v].son[k];if (!tree[v].mark&&!tree[v].last)f[i+1][v]=(f[i+1][v]+f[i][j])%mod;}for (int i=0;i<=tot;i++)ans=(ans+f[m][i])%mod;}int main(){ scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)scanf("%s",s),Insert(s);Make_fail();dp();int x=1;for (int i=1;i<=m;i++)x=(x*26)%mod;cout<<(x-ans+mod)%mod<<endl;return 0;}
感悟:
1.注意這道題有一個last標記,因為可能j不是當前字串的結尾,是別的字串的結尾
【BZOJ 1030】 [JSOI2007]文本產生器