bzoj 1009: [HNOI2008]GT考试【kmp+dp+矩阵快速幂】
看n和k的范围长得就很像矩阵乘法了
设f[i][j]表示到第i个位置的后缀最长匹配目标串的j位。转移的话显然是枚举0~9,然后选择f[i+1]中能被他转移的加起来,需要用到next数组。然后构造矩阵的时候,在转移路径上++即可(注意代码里的f数组只是辅助构造矩阵的,和上文无关
在写挂了n次kmp之后我突然意识到一个问题:k<=20,我随便暴力个5、6次方的都没问题为啥要kmp……
结果还是用了kmp
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int M=30;
int n,m,mod,c[M],ne[M],f[M][M],an;
char s[M];
struct qwe
{
int a[M][M];
void init()
{
memset(a,0,sizeof(a));
}
qwe operator * (const qwe &b) const
{
qwe c;
c.init();
for(int k=0;k<m;k++)
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
c.a[i][j]=(c.a[i][j]+a[i][k]*b.a[k][j])%mod;
return c;
}
}a,sum;
void getne()
{
// int i=0,j=-1;
// ne[0]=-1;
// while(i<m)
// {
// if(c[i]==c[j]||j==-1)
// ne[++i]=++j;
// else
// j=ne[j];
// }
for(int i=1;i<m;i++)
{
int j=ne[i];
while(j&&c[i+1]!=c[j+1])
j=ne[j];
ne[i+1]=c[i+1]==c[j+1]?j+1:0;
}
}
qwe ksm(int b)
{
qwe r;
r.init();
for(int i=0;i<m;i++)
r.a[i][i]=1;
while(b)
{
if(b&1)
r=r*a;
a=a*a;
b>>=1;
}
return r;
}
int main()
{
scanf("%d%d%d%s",&n,&m,&mod,s+1);
for(int i=1;i<=m;i++)
c[i]=s[i]-'0';
getne();
for(int i=0;i<m;i++)
for(int j=0;j<=9;j++)
{
f[i][j]=c[i+1]==j?i+1:f[ne[i]][j];
a.a[i][f[i][j]]++;
}
sum.a[0][0]=1;
sum=sum*ksm(n);
for(int i=0;i<m;i++)
an=(an+sum.a[0][i])%mod;
printf("%d\n",an);
return 0;
}