Tang-Dreamoon's Blog

bzoj 1030 文本生成器

AC自动机+DP

  题目链接:bzoj 1030

  题意:写 B z o j 的题意就是简单——题意见上……

  题解:这道题要求我们求出所有的长度为 M 的包含至少一个已知单词的字符串数量。既然是统计数量 ,我们很自然的想到了动态规划。我们用 F[i][j] 表示用 i 步走到了自动机上 j 号节点,而且从未成功匹配过一次的方案数。这样我们枚举下一位是哪个字母,然后进行转移。如果转移到一个已知单词串的结尾,那么我们将不会累计 F 数组,而是直接将当前的方案数通过乘以 2^(M-i) 累计到答案上。

  我思故我在:在AC自动机上跑动态规划,是常见的思路以及套路。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
int n,m;
int tot=0;
int ans=0;
int mod=10007;
char ap[60000];
int map[66666][30];
int fail[66666];
vector<int> ve[66666];
bool vis[66666];
int dp[111][66666];
int cf[1111];
void trie()
{
int len=strlen(ap+1);
int now=0;
for(int i=1;i<=len;i++)
{
int x=ap[i]-'A';
if(map[now][x]==0) map[now][x]=++tot;
now=map[now][x];
}
vis[now]=1;
// cout<<now<<endl;
}
void ac()
{
queue<int> q;
q.push(0);fail[0]=0;
while(!q.empty())
{
int x=q.front();q.pop();
for(int i=0;i<26;i++)
{
if(map[x][i]!=0)
{
int j=fail[x];
while(j!=0&&map[j][i]==0) j=fail[j];
if(map[j][i]!=0&&map[j][i]!=map[x][i]) fail[map[x][i]]=map[j][i],ve[map[j][i]].push_back(map[x][i]);
else fail[map[x][i]]=0,ve[0].push_back(map[x][i]);
q.push(map[x][i]);
} //else map[x][i]=map[fail[x]][i];
}
}
}
void dfs(int x)
{
for(int i=0;i<ve[x].size();i++)
{
int y=ve[x][i];
vis[y]=(vis[x]||vis[y]);
dfs(y);
}
}
void work()
{
dp[0][0]=1;
for(int i=0;i<m;i++)
{
for(int j=0;j<=tot;j++)
{
if(dp[i][j]!=0)
{
for(int k=0;k<26;k++)
{
int now=j;
while(now!=0&&map[now][k]==0) now=fail[now];
if(map[now][k]!=0)
{
if(vis[map[now][k]]) ans=(ans+(dp[i][j]*cf[m-(i+1)])%mod)%mod;
// if(vis[map[now][k]]) continue;
else dp[i+1][map[now][k]]=(dp[i+1][map[now][k]]+dp[i][j])%mod;
}
else dp[i+1][0]=(dp[i+1][0]+dp[i][j])%mod;
}
}
}
}
}
void make()
{
cf[0]=1;
for(int i=1;i<=m+1;i++)
cf[i]=(cf[i-1]*26)%mod;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",ap+1);
trie();
}
ac();
dfs(0);
make();
work();
cout<<ans<<endl;
return 0;
}