传送门
这个题dp还是很好想的。
f(i,j)表示第i位,模式串匹配到第j位的方案数
f(i,j)=∑k可转移到jf(i−1,k)
如果你已经构造好一个矩阵A,A(i,j)代表i是否可以转移到j
就可以这样:f(i,j)=∑kf(i−1,k)∗A(k,j)
于是这就是线性递推了,明显可以矩阵加速。
现在问题就是怎么构造矩阵。
我们可以发现那个矩阵的定义十分像KMP
我们就可以先构造fail数组
然后枚举每一位,用KMP来匹配,顺便构造矩阵
于是这题就AC了。
代码:
/**************************************************************
Problem: 1009
User: stone41123
Language: C++
Result: Accepted
Time:80 ms
Memory:1300 kb
****************************************************************/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
inline int read(){
int x=0;char ch=' ';int f=1;
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')f=-1,ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
return x*f;
}
int p;
struct matrix{
int n,m;
ll a[30][30];
matrix(){
memset(a,0,sizeof(a));
}
inline matrix operator * (const matrix& y) const {
matrix c;
c.n=n;
c.m=y.m;
for(int i=0;i<c.n;i++){
for(int j=0;j<c.m;j++){
for(int k=0;k<m;k++){
c.a[i][j]=(c.a[i][j]+a[i][k]*y.a[k][j])%p;
}
}
}
return c;
}
}f;
int n,m;
char ch[30];
int P[30];
int fail[30];
void getfail(){
fail[0]=fail[1]=0;
for(int i=1;i<m;i++){
int j=fail[i];
while(j&&P[i]!=P[j])j=fail[j];
if(P[i]==P[j])j++;
fail[i+1]=j;
}
f.n=m;
f.m=m;
for(int i=0;i<m;i++){
for(int j=0;j<=9;j++){
int k=i;
while(k&&P[k]!=j)k=fail[k];
if(P[k]==j)k++;
if(k!=m)f.a[i][k]++;
}
}
}
matrix ksm(matrix A,ll b){
matrix ans=A;
b--;
while(b){
if(b&1){
ans=ans*A;
}
A=A*A;
b>>=1;
}
return ans;
}
int main(){
scanf("%d %d %d",&n,&m,&p);
scanf("%s",ch);
for(int i=0;i<m;i++){
P[i]=ch[i]-'0';
}
getfail();
f=ksm(f,n);
matrix A;
A.n=1;
A.m=m;
A.a[0][0]=1;
A=A*f;
ll ans=0;
for(int i=0;i<m;i++){
ans=(ans+A.a[0][i])%p;
}
printf("%lld",ans);
return 0;
}