[HNOI2008]GT考试
题意
有一个长度为\(n\)\((n\le1e^9)\)只由阿拉伯数字组成的串\(A\),现在给一个长度为\(m\)\((m\le20)\)同样只由阿拉伯数字组成的串\(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 =
\begin{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为例$$\begin{pmatrix}
dp[i][0] & \dots & dp[i][m - 1]
\end{pmatrix}=
\begin{pmatrix}
dp[i - 1][0] & \dots & dp[i - 1][m - 1]
\end{pmatrix}
\begin{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;
}