题意
求有多少个长度为n且仅包含0到9的字符串不包含一个长度为m的子序列。
n<=10^9,m<=20
分析
先得到s的next数组,然后根据dp方程f[n][u]+=f[n-1][i],表示在kmp上走了n步走到节点u的方案数,我们可以构造一个状态转移矩阵,然后矩阵快速幂就好了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define ll long long
#define N 25
using namespace std;
int n,m,MOD,next[N];
char s[N];
struct arr{int a[N][N];}a,b,c;
int getnext(int n)
{
next[0]=-1;next[1]=0;
int i=2,j=0;
while (i<=n)
if (s[j+1]==s[i]||j==-1)
{
j++;
next[i]=j;
i++;
}
else j=next[j];
}
void mul(arr a,arr b)
{
memset(c.a,0,sizeof(c.a));
for (int i=0;i<=m;i++)
for (int j=0;j<=m;j++)
for (int k=0;k<=m;k++)
c.a[i][j]=(c.a[i][j]+(ll)a.a[i][k]*b.a[k][j]%MOD)%MOD;
}
void ksm(int x)
{
if (x==1) return;
ksm(x/2);
mul(c,c);
if (x%2==1) mul(c,a);
}
int main()
{
scanf("%d%d%d",&n,&m,&MOD);
scanf("%s",s+1);
getnext(m);
for (int i=0;i<=m;i++)
for (int j=0;j<=9;j++)
{
int u=i;
while (u&&s[u+1]-'0'!=j) u=next[u];
if (s[u+1]-'0'==j) u++;
if (u==m) continue;
//f[n][u]+=f[n-1][i]
a.a[i][u]++;
}
c=a;
ksm(n);
int ans=0;
for (int i=0;i<m;i++)
ans=(ans+c.a[0][i])%MOD;
printf("%d",ans);
return 0;
}