题面
解法
用KMP的思想来转移dp,并用矩乘优化
- 很明显,我们可以这样设计状态:设 表示现在已经填到第 位,且与不吉利数字已经匹配了 位的方案数。最后的答案显然为
- 考虑如何转移。求出 的前驱节点似乎并没有那么方便,考虑通过 来推后面的节点
- 如果下一位匹配,那么 可以转移到 。如果下一位失配,我们考虑下一位是一个什么字符,假设 可以转移到 。其中, 表示当前匹配到第 位,且在第 位失配,第 位的字符为 的最长前缀=后缀的长度,这个和KMP中 数组的定义几乎是一样的
-
直接暴力求就可以了,
如果想提升代码速度可以写一个KMP - 然而,现在的复杂度最好为 , ,考虑如何优化
- 我们发现,每一次转移的时候, 可以转移到的位置其实和 并没有什么关系,所以我们可以直接对 这一位矩乘,然后就能通过此题啦
- 时间复杂度:
代码
#include <bits/stdc++.h>
#define N 40
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
x = 0; int f = 1; char c = getchar();
while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Matrix {
int a[N][N];
void Clear() {memset(a, 0, sizeof(a));}
};
int n, m, p;
Matrix operator * (Matrix x, Matrix y) {
Matrix ret; ret.Clear();
for (int k = 0; k < m; k++)
for (int i = 0; i < m; i++)
for (int j = 0; j < m; j++)
ret.a[i][j] = (ret.a[i][j] + x.a[i][k] * y.a[k][j] % p) % p;
return ret;
}
Matrix operator ^ (Matrix x, int y) {
Matrix ret = x; y--;
while (y) {
if (y & 1) ret = ret * x;
y >>= 1, x = x * x;
}
return ret;
}
int calc(string st) {
string x = "", y = ""; int ret = 0;
for (int l = 0, r = st.size() - 1; l < st.size(); l++, r--) {
x = x + st[l], y = st[r] + y;
if (x == y && l < st.size() - 1) ret = l + 1;
}
return ret;
}
int main() {
read(n), read(m), read(p);
string st, tmp = ""; cin >> st;
Matrix tx; tx.Clear();
for (int i = 0; i < m; i++) {
tx.a[i][i + 1] = 1;
for (char j = '0'; j <= '9'; j++) {
if (j == st[i]) continue;
tx.a[i][calc(tmp + j)]++;
}
tmp = tmp + st[i];
}
tx = tx ^ n;
Matrix ty; ty.Clear(); ty.a[0][0] = 1;
ty = ty * tx; int ans = 0;
for (int i = 0; i < m; i++) ans = (ans + ty.a[0][i]) % p;
cout << ans << "\n";
return 0;
}