题意
有一个长度为(n)((nle1e^9))只由阿拉伯数字组成的串(A),现在给一个长度为(m)((mle20))同样只由阿拉伯数字组成的串(B),求满足条件的(A)串个数,条件:(B)串不包含在(A)串。
题解
(dp[i][j]=)长度为(i)且末尾(后缀)已经与(B)串首部(前缀)匹配了(j)位的满足条件的串的方案数。则:$$dp[i][j] = sum_{k = 0}^{m - 1}dp[i - 1][k]a[k][j]$$ (a[k][j]=) 已经匹配了(k)位现在再加一个数字而能匹配(j)位的方案数。比如(B)串为(12315),(a[1][1] = 1)就是(1 + ?)能与(12315)匹配(1)位的方案数是1的(?)的取值个数((? = 1))。这里讲的匹配是指(1?)的后缀与(12315)的前缀相同的串的长度。$$a =
egin{pmatrix}
9 & 1 & 0 & 0 & 0 & 0
8 & 1 & 1 & 0 & 0 & 0
8 & 1 & 0 & 1 & 0 & 0
9 & 0 & 0 & 0 & 1 & 0
7 & 1 & 1 & 0 & 0 & 1
end{pmatrix}$$行代表(k),列代表(j)。如何求出(a)数组?既然是在求公共的前缀和后缀,故联想到(KMP)的(Next)数组。具体做法见代码。观察上面的转移方程,系数都是常数且是一次方程,自然而然的转化为矩阵的乘积形式,以(m) = 5,(B)串为:12315为例$$egin{pmatrix}
dp[i][0] & dots & dp[i][m - 1]
end{pmatrix}=
egin{pmatrix}
dp[i - 1][0] & dots & dp[i - 1][m - 1]
end{pmatrix}
egin{pmatrix}
9 & 1 & 0 & 0 & 0
8 & 1 & 1 & 0 & 0
8 & 1 & 0 & 1 & 0
9 & 0 & 0 & 0 & 1
7 & 1 & 1 & 0 & 0
end{pmatrix}$$因为是求不包含(B)串的A串个数,故(a)矩阵的最后一列应该忽略。
const int N = 100005;
int mod;
struct mat {
int t;
int A[22][22];
mat() {
mem(A, 0);
}
void Inite(int m) {
t = m;
}
mat operator * (const mat& tp) {
mat ans;
ans.t = tp.t;
rep(i, 0, t) rep(j, 0, t) rep(k, 0, t) {
ans.A[i][j] += A[i][k] * tp.A[k][j];
ans.A[i][j] %= mod;
}
return ans;
}
void operator = (const mat& tp) {
t = tp.t;
rep(i, 0, t) rep(j, 0, t) A[i][j] = tp.A[i][j];
}
};
int n, m;
int nxt[22];
string s;
mat qpow(mat C, int x) {
mat B;
B.Inite(m);
rep(i, 0, m) B.A[i][i] = 1;
for (; x; C = C * C, x >>= 1) if (x & 1) B = B * C;
return B;
}
void Next(){
nxt[0] = nxt[1] = 0;
rep(i, 1, m) {
int k = nxt[i];
while(k && s[k] != s[i]) k = nxt[k];
nxt[i + 1] = (s[k] == s[i] ? k + 1 : 0);
}
}
int main()
{
cin >> n >> m >> mod >> s;
Next();
mat B;
B.Inite(m);
rep(i, 0, m) for (char j = '0'; j <= '9'; ++j) {
int k = i;
while(k && s[k] != j) k = nxt[k];
if (s[k] == j) k++;
if (k != m) B.A[i][k]++;
}
mat C = qpow(B, n);
mat D;
D.Inite(m);
D.A[0][0] = 1;
C = D * C;
int ans = 0;
rep(i, 0, m) ans = (ans + C.A[0][i]) % mod;
cout << ans << endl;
return 0;
}