Problem
给定 N 个单词和整数 A ,B。合法的串 S 为长度在区间 [A, B]
内,仅有大小写字母和数字构成,至少有一个大写字母、一个小写字母和一个数字,且 N 个单词均无法在该串中找到匹配的子串。求 S 串的可行方案数(对结果 MOD 1000003)。
特殊情况:数字 0 1 3 5 7
可分别视作 o i e s t
进行单词串的匹配。同时匹配时忽略大小写。
限制条件
3 ≤ A ≤ B ≤ 20
0 ≤ N ≤ 50
1 ≤ length(Wi) ≤ 20
N 个单词均只有小写字母
解题思路
DP 处理所有合法情况的可行方案数。
- 枚举每种合法的长度 S 。
- 记录是否存在小写字母|大写字母|数字,通过状压记录
- 判断该串的后续状态是否会产生单词串的匹配(使用 AC 自动机处理)。
dp[S][status][idx]
表示长度为 S ,大小写和数字状态为 status ,枚举的第 S 个字符在 AC 自动机中所属节点编号为 idx。
其后续状态有枚举 A-Z
, a-z
, 0-9
。其中 A-Z
由于忽略大小写应视为 a-z
,部分数字应直接处理成对应字母进行处理。
若有 idx 推导的状态加一个字符不会产生单词匹配,则 dp[S][status][idx]
对其后续产生贡献,否则贡献为 0 。
代码
#include<bits/stdc++.h>
using namespace std;
const int MOD = 1e6 + 3;
const int maxn = 1010;
const int CH = 128;
int A, B, N, dp[22][8][1010];
char buf[22];
string str = "oi2e4s6t89";
struct Trie {
int nxt[maxn][CH], fail[maxn], end[maxn];
int root, L;
void init() {
L = 0;
root = newnode();
}
int newnode() {
for(int i=0;i<CH;i++) nxt[L][i] = -1;
end[L++] = 0;
return L-1;
}
void insert(char buf[], int idx) {
int p = root;
for(int i=0;buf[i];i++) {
if(nxt[p][buf[i]] == -1)
nxt[p][buf[i]] = newnode();
p = nxt[p][buf[i]];
}
end[p] = idx;
}
void build() {
queue<int> que;
fail[root] = root;
for(int i=1;i<CH;i++)
if(nxt[root][i] == -1)
nxt[root][i] = root;
else
fail[nxt[root][i]] = root,
que.push(nxt[root][i]);
while(!que.empty())
{
int p = que.front();
que.pop();
for(int i=0;i<CH;i++)
if(nxt[p][i] == -1)
nxt[p][i] = nxt[fail[p]][i];
else {
fail[nxt[p][i]] = nxt[fail[p]][i];
que.push(nxt[p][i]);
}
}
}
} ac;
int findAndJugNxt(int nxt, char c) {
while(nxt != 0 && ac.nxt[nxt][c] == -1) nxt = ac.fail[nxt];
nxt = ac.nxt[nxt][c];
int tmpNxt = nxt;
while(tmpNxt != 0) {
if(ac.end[tmpNxt] != 0) return -1;
tmpNxt = ac.fail[tmpNxt];
}
return nxt;
}
int main()
{
scanf("%d %d %d", &A, &B, &N);
ac.init();
for(int i=1;i<=N;i++)
{
scanf(" %s", buf);
ac.insert(buf, i);
}
ac.build();
dp[0][0][0] = 1;
for(int S=0;S<B;S++)
for(int status=0;status<8;status++)
for(int idx=0, nxt;idx<ac.L;idx++)
{
if(dp[S][status][idx] == 0) continue;
for(char c='a', tc;c<='z';c++) //A-Z for ingore upper or lower
{
nxt = findAndJugNxt(idx, c);
if(nxt != -1)
(dp[S+1][status|1][nxt] += dp[S][status][idx]) %= MOD;
}
for(char c='a';c<='z';c++)
{
nxt = findAndJugNxt(idx, c);
if(nxt != -1)
(dp[S+1][status|2][nxt] += dp[S][status][idx]) %= MOD;
}
for(char c='0', tc;c<='9';c++)
{
tc = str[c-'0'];
nxt = findAndJugNxt(idx, tc);
if(nxt != -1)
(dp[S+1][status|4][nxt] += dp[S][status][idx]) %= MOD;
}
}
int ans = 0;
for(int S=A;S<=B;S++)
for(int idx=0;idx<ac.L;idx++)
(ans += dp[S][7][idx]) %= MOD;
printf("%d\n", ans);
}