解题思路
求一个序列,要求其中没有任意一段与给定串匹配。
暴力做法:
f[i][j]
f
[
i
]
[
j
]
表示枚举到序列的第
i
i
个位置,在给定串中匹配到第个位置的方案数。在第
i+1
i
+
1
个位置放
0
0
~时会令
j
j
发生改变,改变到多少需要用到数组。
对于给定串的任意位置 i i ,预处理在第个位置的十种选择方案中,转移到位置 j j 的情况有几种,可以视作行 M+1 M + 1 列矩阵 G G 。
设一行列的初始矩阵
C
C
为,
其中第i个位置表示已经在给定串中匹配到第
i
i
个位置的方案数。
显然初始状态下只有是
1
1
,其余都是。
这样原来的暴力转移方式就可以用矩阵乘法代替,
将
C
C
GN
G
N
得到的矩阵中前
M−1
M
−
1
个位置上的数加起来就是答案。
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<cmath>
#include<cstdlib>
using namespace std;
char a[25];
int n,m,mod,nex[25];
struct ldx{
int s[25][25];
ldx operator *(const ldx &p)const{
ldx r0;
for(int i=0;i<=m;i++)
for(int j=0;j<=m;j++){
r0.s[i][j]=0;
for(int k=0;k<=m;k++) r0.s[i][j]=(r0.s[i][j]+s[i][k]*p.s[k][j]%mod)%mod;
}
return r0;
}
}c;
void getnex(){
nex[0]=-1;
for(int i=1;i<m;i++){
int j=nex[i];
while(j!=-1 && a[j]!=a[i]) j=nex[j];
nex[i+1]=j+1;
}
}
ldx ksm(int num){
if(num==1) return c;
ldx j=ksm(num/2);
if(num&1) return j*j*c;
else return j*j;
}
int main(){
int ans=0;
scanf("%d%d%d%s",&n,&m,&mod,a);getnex();
for(int i=0;i<m;i++)
for(int k=0;k<=9;k++){
int j=i;
while(j!=-1 && a[j]!='0'+k) j=nex[j];
c.s[i][j+1]=(c.s[i][j+1]+1)%mod;
}
c=ksm(n);
for(int i=0;i<m;i++) ans=(ans+c.s[0][i])%mod;
printf("%d",ans);
return 0;
}