給定上限 N(長度 ≤ 1200)和禁忌字符串集合 S(共 M ≤ 100 個,總長度 L ≤ 1500)。若一個正整數的十進制表示不包含 S 中任一字符串作為子串,則稱為「幸運數」。求不超過 N 的幸運數個數,答案模 10⁹+7。
這是「敏感詞過濾」與「數位DP」的經典結合:
pos(已處理前幾位)node(當前匹配狀態)N 限制 tight(前綴是否等於 N 的前綴)matched(實際上只要避開標記為禁忌的節點即可)t[1][0]dp[pos][node][tight] 表示前 pos 位、自動機處於 node、受限狀態為 tight 的合法方案數。
tight = 0:已經比 N 的前綴小,後續位可任選 0-9tight = 1:前綴與 N 完全相同,當前位上限為 N[pos]轉移時枚舉下一位數字 d,沿自動機邊 go[node][d] 跳轉,若目標節點被標記為禁忌(v[target]),則跳過。
// AC自動機構建(BFS構建失配指針)
void bfs() {
queue q; q.push(1);
while (!q.empty()) {
int u = q.front(); q.pop();
v[u] |= v[fail[u]]; // 繼承失配節點的禁忌標記
for (int c = 0; c < 10; ++c) {
if (trie[u][c]) {
fail[trie[u][c]] = trie[fail[u]][c];
q.push(trie[u][c]);
} else {
trie[u][c] = trie[fail[u]][c]; // 補全轉移邊
}
}
}
}
// 數位DP
// f[i][j][k]: 前i位,自動機節點j,tight=k的方案數
// 分兩階段:位數 < len(N) 和位數 == len(N)
// 詳見原代碼完整實現
位數(≤1200) × 節點數(≤1501) × 2 ≈ 3.6M2 × 節點數 × 2trie[1][0]v[u] |= v[fail[u]] 確保失配路徑上的禁忌也被標記1e9+7