BZOJ1030:[JSOI2007]文本生成器(AC自動機,DP)


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 Input

2 2
A
B

Sample Output

100

Solution

第一次做在AC自動機上跑DP的題……
之前做的好像都是亂搞
學長說AC自動機的DP都非常套路
大部分f[i][j]表示當前在節點j,且串長為i時的情況,
有時再加一維表示這個狀態里面包含了哪些東西
而且AC自動機的DP會經常讓你用矩陣乘法優化
 
那么對於這個題,我們可以先將AC自動機建立出來,然后搞一個簡單的容斥
用所有的情況減去不可讀的情況。
那么那些是不可讀的情況呢?當然就是跑不到單詞結尾節點的情況嘍……
定義f[i][j]表示當前在j點且串長為i時不經過單詞結尾的路徑條數
然后從父親往兒子轉移即可
注意如果一個單詞的后綴是一個可讀單詞(即fail指針指向可讀單詞的結尾節點)
那么這個單詞一定也是可讀的,我們就不能往這個單詞走了
話說之前我好像做過類似的不過沒有吸取教訓QvQ活該WA好久

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<queue>
 5 #define MOD (10007)
 6 #define  N  (10005)
 7 using namespace std;
 8 
 9 int Son[N][26],End[N],Fail[N];
10 int n,m,sz,f[105][N],ans;
11 char s[N];
12 queue<int>q;
13 
14 void Insert(char s[])
15 {
16     int now=0,len=strlen(s);
17     for (int i=0; i<len; ++i)
18     {
19         int x=s[i]-'A';
20         if (!Son[now][x]) Son[now][x]=++sz;
21         now=Son[now][x];
22     }
23     End[now]|=1;
24 }
25 
26 void Build_Fail()
27 {
28     for (int i=0; i<26; ++i)
29         if (Son[0][i])
30             q.push(Son[0][i]);
31     while (!q.empty())
32     {
33         int now=q.front();
34         q.pop();
35         for (int i=0; i<26; ++i)
36         {
37             if (!Son[now][i])
38             {
39                 Son[now][i]=Son[Fail[now]][i];
40                 continue;
41             }
42             End[Son[now][i]]|=End[Son[Fail[now]][i]];
43             Fail[Son[now][i]]=Son[Fail[now]][i];
44             q.push(Son[now][i]);
45         }
46 
47     }
48 }
49 
50 int main()
51 {
52     scanf("%d%d",&n,&m);
53     for (int i=1; i<=n; ++i)
54         scanf("%s",s),Insert(s);
55     Build_Fail();
56     f[0][0]=1;
57     for (int i=1; i<=m; ++i)
58         for (int j=0; j<=sz; ++j)
59             for (int k=0; k<26; ++k)
60                 if (!End[Son[j][k]])
61                     (f[i][Son[j][k]]+=f[i-1][j])%=MOD;
62     for (int i=0; i<=sz; ++i)
63         (ans+=f[m][i])%=MOD;
64     int sum=1;
65     for (int i=1; i<=m; ++i)
66         sum=sum*26%MOD;
67     printf("%d\n",(sum-ans+MOD)%MOD);
68 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM