题目链接:
https://www.lydsy.com/JudgeOnline/problem.php?id=1009
题意:
准考证号为\(n\)位数\(X_1X_2....X_n(0<=X_i<=9)\),你不希望准考证号上出现不吉利的数字。
你的不吉利数学\(A_1A_2...A_m(0<=A_i<=9)\)有\(m\)位,不出现是指\(X_1X_2...X_n\) 中没有恰好一段等于\(A_1A_2...A_m\). A_1$ 和 \(X_1\)可以为\(0\)。
问你不出现不吉利数字的号码有多少种,输出模\(mod\)取余的结果。
\(n<=10^9,M<=20,mod<=1000\)。
题解:
对于这种间接的多模式匹配字符串+计数问题,我们可以用\(AC\)自动机+\(dp\)。
\(dp[i][j]\)表示串长为 \(i\) , 匹配到了 \(AC\) 自动机的节点 \(j\),并且从来没有出现过完整的不吉利号码的方案数。
容易知道,\(dp\) 形式就是这样:
dp[0][0] = 1;
for(int i = 1; i <= n; i++) {
for(int j = 0; j <= sz; j++) {
for(int k = 0; k <= 9; k++) {
int v = ch[j][k];
if(End[v] == 0) {
dp[i][v] += dp[i - 1][j];
dp[i][v] %= mod;
}
}
}
}
int res = 0;
for(int i = 0; i <= sz; i++) {
res += dp[n][i];
res %= mod;
}
std::cout << "res = " << res << '\n';
但是\(N<=10^9\),显然复杂度会爆炸。
所以可以用矩阵快速幂来优化加速\(dp\)。
初始化矩阵\(tmp = [1,0,0]\),意思和\(dp[0][0] = 1\)的初始化一样。
将 \(x\) 节点与\(x\)的所有儿子节点\(son\)在转移矩阵\(a\) 中 \(a[x][son]+=1\)。
这样在矩阵乘法的转移时,可以把父亲的状态装一到儿子。
最后答案即为\(tmp * a ^ n\)。