密碼長度 n (≤25),小寫字母組成。給定 m (≤10) 個魔法單詞,密碼需包含至少 k 個魔法單詞(可重疊)。求合法密碼數模 20090717。
dp[pos][node][mask] 表示構建到第 pos 位,自動機處於 node,已匹配單詞集合為 mask 的方案數。sum(dp[n][*][mask]) 其中 mask 中 1 的個數 ≥ k。
const int MAXN = 150; // 節點上限:10個單詞 × 長度10
const int SIGMA = 26;
int ch[MAXN][SIGMA]; // 轉移
int val[MAXN]; // 輸出 bitmask
int fail[MAXN]; // 失敗指針
int last[MAXN]; // 輸出鏈
int sz = 1; // 節點計數
val 設置對應 bit (1<<i)。fail[u] = ch[fail[parent]][c],合併輸出 val[u] |= val[fail[u]],設置 last[u] 指向最近有輸出的祖先。
dp[0][0][0] = 1;
for (int i = 0; i < n; ++i)
for (int u = 0; u < sz; ++u)
for (int mask = 0; mask < (1<
popcount[mask] 加速最終統計。
#include <bits/stdc++.h>
using namespace std;
const int MOD = 20090717;
const int MAXN = 150, SIGMA = 26, MAXM = 11, MAXL = 30;
int ch[MAXN][SIGMA], val[MAXN], fail[MAXN], last[MAXN], sz;
int dp[MAXL][MAXN][1<>1] + (i&1);
int n, m, k;
while (cin >> n >> m >> k, n||m||k) {
sz = 1; memset(ch[0], 0, sizeof ch[0]); val[0]=0;
for (int i = 0; i < m; ++i) {
string s; cin >> s; insert(s, i);
}
build();
cout << solve(n, m, k) << '\n';
}
}
本題是「多模式串匹配計數」的經典模型:AC 自動機處理重疊匹配,DP 狀態壓縮記錄已匹配集合。關鍵在於正確構建 fail 指針與輸出鏈合併,以及 DP 狀態設計無遺漏無重複。模板化後可解決所有「字符串包含至少 k 個給定模式」類問題。