题意
给定长度为m的数字串s,求不包含子串s的长度为n的数字串的数量
思路
状态转移方程
dp[i][j]=Σ dp[i−1][k]∗a[k][j] (0<k<m)
d
p
[
i
]
[
j
]
=
Σ
d
p
[
i
−
1
]
[
k
]
∗
a
[
k
]
[
j
]
(
0
<
k
<
m
)
dp[i][j]
d
p
[
i
]
[
j
]
代表当前长度为i的字符串, 后j位和目的串的前j位匹配,这样的串有多少种
a[k][j]
a
[
k
]
[
j
]
代表当前字符串后k位与目的串匹配, 再加上新的一位使字符串的后缀变成j个,这样的状态转移有多少种
所以
i−1
i
−
1
的串后缀为
k
k
有
dp[i−1][k]
d
p
[
i
−
1
]
[
k
]
种,乘上转移的种类数,就是长度为
i
i
的串有多少种了
矩阵快速幂优化dp
由上式可得
dp[n][j]=Σ dp[0][k]∗(a[k][j])n
d
p
[
n
]
[
j
]
=
Σ
d
p
[
0
]
[
k
]
∗
(
a
[
k
]
[
j
]
)
n
因此求结果我们直接用快速幂计算
a
a
矩阵的n次方即可
而初始条件
dp[0][0]=1
d
p
[
0
]
[
0
]
=
1
除此之外
dp[0][x]=0
d
p
[
0
]
[
x
]
=
0
所以上式变成
dp[n][j]=dp[0][0]∗(a[0][j])n
d
p
[
n
]
[
j
]
=
d
p
[
0
]
[
0
]
∗
(
a
[
0
]
[
j
]
)
n
最终结果对所有的j求和,表示长度为n的字符串后缀为0~m-1的种类数,即我们要求的可行的字符串数
KMP
现在解决转移矩阵
a
a
的求法
首先看一个a的样例
s=12312
s
=
12312
9 1 0 0 0 0
8 1 1 0 0 0
8 1 0 1 0 0
9 0 0 0 1 0
8 1 0 0 0 1
a[0][0]=9
a
[
0
]
[
0
]
=
9
代表当前匹配0位,转移到匹配0位有9种,即除了1以外的所有数字
a[1][0]=8
a
[
1
]
[
0
]
=
8
从1位到0位,即除了1和2以外的所有数. ( +1匹配到1, +2匹配到2 )
a[0][1]=1
a
[
0
]
[
1
]
=
1
从0位到1位, 只能是1
求法见代码(kmp不熟)
代码
#include <algorithm>
#include <bits/stdc++.h>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int INF = 0x7f7f7f7f;
const int maxn = 100 + 10;
int MOD;
int n, m; //需要长度,给定串长度
char s[ maxn ];
int nex[ maxn ];
struct MAtrix {
int xx[ 22 ][ 22 ];
int *operator[] ( int x ) { return xx[ x ]; }
} a, b;
void operator*= ( MAtrix &x, MAtrix &y ) {
MAtrix z;
memset ( &z, 0, sizeof ( z ) );
for ( int i = 0; i < m; ++i )
for ( int j = 0; j < m; ++j )
for ( int k = 0; k < m; ++k )
z[ i ][ j ] = ( z[ i ][ j ] + x[ i ][ k ] * y[ k ][ j ] ) % MOD;
x = z;
}
void kmp () {
int fix = 0;
for ( int i = 2; i <= m; ++i ) {
while ( fix && s[ fix + 1 ] != s[ i ] ) //匹配失败,回到前面可以再匹配的位置
fix = nex[ fix ];
if ( s[ fix + 1 ] == s[ i ] ) //匹配成功,fix往下走
++fix;
nex[ i ] = fix;
}
for ( int i = 0; i < m; ++i ) //枚举i,当前末尾匹配了几位,求b[ i ][ k ];
for ( char j = '0'; j <= '9'; ++j ) {
fix = i;
while ( fix && s[ fix + 1 ] != j ) //匹配失败,转到下一个匹配的位置
fix = nex[ fix ];
if ( j == s[ fix + 1 ] ) //匹配成功,从i到当前可以匹配的位置(默认为i+1),可能的路径++
b[ i ][ fix + 1 ]++;
else
b[ i ][ 0 ]++; //直到fix走到最头也无法匹配成功,只能转移到0位匹配
}
}
void quick_pow ( int x ) {
while ( x ) {
if ( x & 1 )
a *= b;
b *= b;
x >>= 1;
}
}
int main () {
#ifdef LOCAL
freopen ( "in", "r", stdin );
// freopen("out","w",stdout);
#endif
scanf ( "%d%d%d", &n, &m, &MOD );
scanf ( "%s", s + 1 );
kmp ();
for ( int i = 0; i < m; ++i )
a[ i ][ i ] = 1; //单位矩阵,求b矩阵的n次幂
quick_pow ( n ); // b ^ n;
int ans = 0;
for ( int i = 0; i < m; ++i )
ans = ( ans + a[ 0 ][ i ] ) % MOD;//最终结果
printf ( "%d\n", ans );
return 0;
}