1305. GT考试
题意:
准考证长度为n位,不吉利数字有m位,问不出现不吉利数字的准考证有多少种,答案mod K
题解:
动态规划+kmp+矩阵快速幂
设dp[i][j]表示长度为i,且不包含S串,且末尾部分与S串的前缀匹配的最大长度是j的所有字符串的集合
S串为不吉利数字
现在相同部分为j,再黄色串后面再添加一个新数,有10种选择0~9,
如果添加的正好等于红色对应部分,那j++
如果不等于,就重新匹配最长部分,该如何快速匹配?用kmp的next数组,k=next[k],实现快速匹配最长长度
转移方程:
dp[i+1][k]+=dp[i][k]
我们现在考虑两层状态之间的关系,即dp[i+1][…]与dp[i][…]的关系,我们可以列出这个式子
dp[i+1,0]=a00 * dp[i,0] +a01 * dp[i,1]…+
dp[i+1,1]=a10 * dp[i,0] + a11 * dp[i,1]+…+
而这些a可以组成一个矩阵A
由此可以得到:
dp[i+1] = dp[i] *A
所以dp[n] = f[n-1] *A =…=F[0] * An
F[0]表示长度为0的情况,F[0] = 1
A怎么得到?我们已经说过, dp[i+1][k]+=dp[i][k],也就是dp[i+1] = dp[i] *A,所以我们就把dp[i][k]加到对应的小a上即可,也就是动态规划里的状态我们加到A矩阵里,然后A求n-1次幂(矩阵快速幂)
代码:
#include<bits/stdc++.h>
#define debug(a,b) printf("%s = %d\n",a,b)
typedef long long ll;
using namespace std;inline int read(){int s=0,w=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();//s=(s<<3)+(s<<1)+(ch^48);return s*w;
}
const int maxn=35;
int n,m,mod;
char str[maxn];
int ne[maxn];
int a[maxn][maxn];
void mul(int c[][maxn],int a[][maxn],int b[][maxn])
{static int t[maxn][maxn];memset(t,0,sizeof(t));for(int i=0;i<m;i++){for(int j=0;j<m;j++){for(int k=0;k<m;k++){t[i][j]=(t[i][j]+a[i][k]*b[k][j])%mod;}}}memcpy(c,t,sizeof(t));
}
int qmi(int k)
{int f0[maxn][maxn]={1};while(k){if(k&1)mul(f0,f0,a);//f0=f0*amul(a,a,a);//a=a*ak>>=1;}int res=0;for(int i=0;i<m;i++)res=(res+f0[0][i])%mod;return res;
}
int main()
{cin>>n>>m>>mod;cin>>str+1;for(int i=2,j=0;i<=m;i++){while(j&&str[j+1]!=str[i])j=ne[j];if(str[j+1]==str[i])j++;ne[i]=j; }//初始化A矩阵 for(int j=0;j<m;j++){for(int c='0';c<='9';c++){int k=j;while(k&&str[k+1]!=c)k=ne[k];if(str[k+1]==c)k++;if(k<m)a[j][k]++;}}//F[n]=F[0]*A^ncout<<qmi(n)<<endl;return 0;
}