Description
阿申准备报名参加GT考试,准考证号为N位数X1X2….Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2…Am(0<=Ai<=9)有M位,不出现是指X1X2…Xn中没有恰好一段等于A1A2…Am. A1和X1可以为0
Input
第一行输入N,M,K.接下来一行输入M位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6
Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.
Sample Input
4 3 100
111
Sample Output
81
HINT
Source
设dp[i][j]为第i个号码匹配到第j个不吉利数字的方案数。
显然前i个号码的后缀构成了前j个不吉利数字,而dp[i][j]向i+1转移相当于在后缀后面新加一个字符,可以联想到AC自动机,然而这个只有一个串所以kmp就足够了。
设a[k][j]为k位后面加一个字母转移到j的方案数,于是:
dp[i][j]=∑0<=k<=m−1dp[i−1][k]∗a[k][j]
我们发现k后面加一个字母转移到j可以用kmp实现。
这个式子是线性的,可以用矩阵优化。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int SZ = 1000010;
const int INF = 1000000010;
int n,m,mod,nxt[233];
char s[233];
void getnxt(char s[])
{
int l = strlen(s);
nxt[0] = nxt[1] = 0;
for(int i = 1;i < l;i ++)
{
int j = nxt[i];
while(j && s[i] != s[j]) j = nxt[j];
nxt[i + 1] = s[i] == s[j] ? j + 1 : 0;
}
}
struct matrix{
int n,m,num[30][30];
matrix(int a,int b) : n(a),m(b) {memset(num,0,sizeof(num));}
};
matrix operator *(const matrix &a,const matrix &b)
{
matrix ans(a.n,b.m);
for(int i = 0;i < ans.n;i ++)
for(int j = 0;j < ans.m;j ++)
for(int k = 0;k < a.m;k ++)
ans.num[i][j] = (ans.num[i][j] + (LL)a.num[i][k] * b.num[k][j] % mod) % mod;
return ans;
}
matrix ksm(matrix a,int b)
{
matrix ans(a.n,a.m);
for(int i = 0;i < ans.n;i ++)
ans.num[i][i] = 1;
while(b)
{
if(b & 1) ans = ans * a;
a = a * a;
b >>= 1;
}
return ans;
}
int main()
{
scanf("%d%d%d%s",&n,&m,&mod,s);
getnxt(s);
matrix f(m,m);
for(int i = 0;i < m;i ++)
{
for(char j = '0';j <= '9';j ++)
{
int k = i;
while(k && s[k] != j) k = nxt[k];
if(s[k] == j) k ++;
if(k != m) f.num[i][k] ++;
}
}
matrix fn = ksm(f,n),a(1,m);
a.num[0][0] = 1;
a = a * fn;
int ans = 0;
for(int i = 0;i < m;i ++)
ans = (ans + a.num[0][i]) % mod;
printf("%d\n",ans);
return 0;
}