f[i][j]表示准考证号前i位的后j位与不吉利串的前j位相等
那么答案就是
转移方程:
a[i][j]表示不吉利串的第i位后加 从0~9之间的数之后变为不吉利串第j位的方案数
求a[i][j] 用kmp
大概意思就是:
(1):先跑一遍kmp 把不吉利串的fail 都先求出来
(2):for(i 0→m-1)
for(p 0→9)
求不吉利串前i位+p 的fail值
其fail值即是第i位后加上p可以转移到的位数
a[i][j] 是一个矩阵,f[i][j]也是一个矩阵(只取其中的lenm 段,相当于使其滚动起来)
↓
然后用矩阵快速幂
#include<cstdio>
#include<cstring>
#include<iostream>
#define ll long long
using namespace std;
int n,lenw,kk;
char w[31];
ll a[31][31];
int fail[31];
ll f[31][31];
void kmp()
{
for(int i=2,j=0;i<=lenw;++i)
{
while(j&&w[i-1]!=w[j])
j=fail[j];
if(w[i-1]==w[j])
fail[i]=++j;
}
for(int i=0;i<lenw;++i)
for(int p=0;p<=9;++p)
{
int k=i;
while(k&&w[k]!=p+'0')
k=fail[k];
if(w[k]==p+'0')
++k;
++a[i][k];
}
/*for(int i=0;i<=lenw;i++)
{
//printf("\n");
for(int j=0;j<=lenw;j++)
printf("%d ",a[i][j]);
printf("\n");
}*/
}
int temp[31][31];
void out11()
{
printf("\n");
for(int i=0;i<=lenw;i++)
{
//printf("\n");
for(int j=0;j<=lenw;j++)
printf("%d ",f[i][j]);
printf("\n");
}
printf("\n");
//printf("%d",ans);
}
int main(){
//freopen("bzoj_1009.in","r",stdin);
//freopen("bzoj_1009.out","w",stdout);
scanf("%d%d%d%s",&n,&lenw,&kk,w);
kmp();
for(int i=0;i<=lenw;i++)
f[i][i]=1;
while(n)
{
if(n&1)
{
for(int i=0;i<=lenw;i++)
for(int j=0;j<=lenw;j++)
{
temp[i][j]=0;
for(int k=0;k<=lenw;k++)
temp[i][j]+=(f[i][k]*a[k][j])%kk;
}
for(int i=0;i<=lenw;i++)
for(int j=0;j<=lenw;j++)
f[i][j]=temp[i][j];
}
for(int i=0;i<=lenw;i++)
for(int j=0;j<=lenw;j++)
{
temp[i][j]=0;
for(int k=0;k<=lenw;k++)
temp[i][j]+=(a[i][k]*a[k][j])%kk;
}
for(int i=0;i<=lenw;i++)
for(int j=0;j<=lenw;j++)
a[i][j]=temp[i][j];
n>>=1;
//out11();
}
ll ans=0;//cout<<0;
for(int i=0;i<lenw;i++)
{
ans+=f[0][i];
ans%=kk;
}
printf("%lld",ans);
//while(1);
return 0;
}