洛谷P4052 [JSOI2007]文本生成器 (AC自动机+ dp)
题目链接:https://www.luogu.com.cn/problem/P4052
套路题,利用 \(fail\) 指针转移 \(dp\)
首先是正难则反,计算不包含可读单词的方案数
设 \(dp[i][j]\) 表示长度为 \(i\) 的文本,当前在 \(j\) 节点,不经过单词节点的方案数
直接在 \(trie\) 图上转移即可
注意:如果一个节点单词的后缀是单词节点,那这个节点也不能经过
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn = 10010;
const int M = 10007;
int n, m, rt = 0, tot = 0;
ll ans = 0;
struct Node{
int son[30], sz, fail;
}t[maxn];
int dp[110][maxn];
char s[maxn];
void insert(){
int p = rt;
int len = strlen(s);
for(int i = 0 ; i < len ; ++i){
if(!t[p].son[s[i] - 'A']){
t[p].son[s[i] - 'A'] = ++tot;
}
p = t[p].son[s[i] - 'A'];
}
t[p].sz = 1;
}
queue<int> q;
void build(){
t[rt].fail = rt;
for(int i = 0 ; i <= 25 ; ++i){
if(t[rt].son[i]) q.push(t[rt].son[i]);
}
while(!q.empty()){
int u = q.front(); q.pop();
t[u].sz |= t[t[u].fail].sz;
for(int i = 0 ; i <= 25 ; ++i){
if(t[u].son[i]){
t[t[u].son[i]].fail = t[t[u].fail].son[i];
q.push(t[u].son[i]);
} else{
t[u].son[i] = t[t[u].fail].son[i];
}
}
}
}
int qsm(int i, int po){
int res = 1;
while(po){
if(po & 1) res = 1ll * res * i % M;
po >>= 1;
i = 1ll * i * i % M;
}
return res;
}
ll read(){ ll s = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar(); } return s * f; }
int main(){
n = read(), m = read();
for(int i = 1 ; i <= n ; ++i){
scanf("%s", s);
insert();
}
build();
dp[0][0] = 1;
for(int i = 1 ; i <= m ; ++i){
for(int j = 0 ; j <= tot ; ++j){
for(int k = 0 ; k <= 25 ; ++k){
if(!t[t[j].son[k]].sz) dp[i][t[j].son[k]] = (dp[i][t[j].son[k]] + dp[i - 1][j]) % M;
}
}
}
int ans = 0;
for(int i = 0 ; i <= tot ; ++i){
ans = (ans + dp[m][i]) % M;
}
printf("%d\n", ((qsm(26, m) - ans) % M + M) % M);
return 0;
}