题意:b为非法数字b[1]...b[m],其位数<=20.某个数有n位,每位可以填入[0,9].
n<=1e9 问有多少种填数字方案,满足n位中没有非法数字m出现?
先不考虑n范围 则可以设置dp[i][j]表示放了合法放了前i位,其后缀j和非法的前j位相同的方法数.
转移状态显然是一位一位添加
显然dp[i+1][j+1]+=dp[i][j] 添加b[j+1]
若新添加的a[i+1]!=b[j+1] 算出此时a的后缀能匹配b的最长前缀是多少.
联想到kmp,枚举匹配到i然后再枚举新添加字符a[i+1]后,算出此时能匹配的最长前缀.从而得到矩阵p.
p[k][j]表示添加一个字符,使匹配前k个字符到匹配前j个字符的方案数.
则dp方程表示为 dp[i][j]= sigma(dp[i-1][k]*p[k][j]) (k=[0,m-1])
n<=1e9 添加n次 则利用矩阵幂优化.答案为a[i][0]累加(初始匹配0个到i个)
n<=1e9 问有多少种填数字方案,满足n位中没有非法数字m出现?
先不考虑n范围 则可以设置dp[i][j]表示放了合法放了前i位,其后缀j和非法的前j位相同的方法数.
转移状态显然是一位一位添加
显然dp[i+1][j+1]+=dp[i][j] 添加b[j+1]
若新添加的a[i+1]!=b[j+1] 算出此时a的后缀能匹配b的最长前缀是多少.
联想到kmp,枚举匹配到i然后再枚举新添加字符a[i+1]后,算出此时能匹配的最长前缀.从而得到矩阵p.
p[k][j]表示添加一个字符,使匹配前k个字符到匹配前j个字符的方案数.
则dp方程表示为 dp[i][j]= sigma(dp[i-1][k]*p[k][j]) (k=[0,m-1])
n<=1e9 添加n次 则利用矩阵幂优化.答案为a[i][0]累加(初始匹配0个到i个)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=35;
int n,m,mod;
int fail[N];
char s[N];
int a[N][N],b[N][N];
void mul(int a[N][N],int b[N][N],int ans[N][N])
{
int tmp[N][N];
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
{
tmp[i][j]=0;
for(int k=0;k<m;k++)
tmp[i][j]=(tmp[i][j]+a[i][k]*b[k][j])%mod;
}
for(int i=0;i<m;i++)
for(int j=0;j<m;j++)
ans[i][j]=tmp[i][j];
}
int main()
{
cin>>n>>m>>mod;
scanf("%s",s+1);
int j=0;
for(int i=2;i<=m;i++)//init_fail
{
while(j>0&&s[j+1]!=s[i])
j=fail[j];
if(s[j+1]==s[i])
j++;
fail[i]=j;
}
for(int i=0;i<m;i++)//
{
for(int j=0;j<=9;j++)//add new a[i+1]=j
{
int t=i;
while(t>0&&s[t+1]-'0'!=j)
t=fail[t];
if(s[t+1]-'0'==j)
t++;
if(t!=m)
b[t][i]=(b[t][i]+1)%mod;//i->t
}
}
for(int i=0;i<m;i++)
a[i][i]=1;
while(n)
{
if(n&1)
mul(a,b,a);
mul(b,b,b);
n>>=1;
}
int sum=0;
for(int i=0;i<m;i++)//
sum=(sum+a[i][0])%mod;//
cout<<sum<<endl;
return 0;
}