当前位置: 代码迷 >> 综合 >> [bzoj1009][kmp][DP][矩阵乘法]GT考试
  详细解决方案

[bzoj1009][kmp][DP][矩阵乘法]GT考试

热度:45   发布时间:2023-12-19 06:02:24.0

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位的数。 N<=10^9,M<=20,K<=1000

Output

  阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.

Sample Input

4 3 100
111

Sample Output

81

题解

好久之前的题了。。
第一眼看这题。。是不是不知所措!是你就对了,因为我就是这样的
首先观察一下题目,简化题意就是一个给定长度的串,要求这个串里面不包含另一个给出的串,求这种串有多少个。Kmp!
暴力就可以枚举所有串的情况然后kmp。。当然这样会爆炸
高级一点的,我们来个Dp
设f[i][j]为 长度为i的母串的后缀与子串匹配到了j个长度的方案数
很明显,答案就是Sigma(f[n][0]~f[n][m-1])对吧
再给出一个数组w[i][j],表示在子串长度为i的前缀后加一个数,使这个前缀+这个数所组成的串与子串从头开始所能匹配的长度为j的方案数
转移方程就是f[i][j]*w[j][k]->f[i+1][k]
这样的复杂度就是n*m^2的,时间空间都会炸
考虑优化,转移方程中可以看出f[i+1]只会与f[i]有关,那么可以滚动。空间过得去时间过不去而已
你既然都想到空间优化居然想不到时间可以矩乘
每个f[i][j]都要乘w[j]这个数组给后面做贡献,且w数组是不变的。那么就矩乘优化啦!
代码中可以直接用转移矩阵代替w矩阵,因为转移矩阵其实就是w矩阵调转一个方向。至于为什么这样我推荐你手推一下矩阵
有个小槽点,w[i][m]这个状态是不能继承的,因为这样母串中一定会出现子串。而w[i][0]这个状态可继承。所以矩乘的时候枚举为0~m-1

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct matrix
{int m[25][25];matrix(){
   memset(m,0,sizeof(m));}
}st;
int n,m,Mod;
matrix multi(matrix u,matrix v,int n,int m,int p)
{matrix ret;for(int i=0;i<n;i++)for(int j=0;j<m;j++)for(int k=0;k<p;k++)ret.m[i][k]=(ret.m[i][k]+u.m[i][j]*v.m[j][k])%Mod;return ret;
}
matrix pow_mod(matrix u,int b)
{matrix ans;for(int i=0;i<m;i++)ans.m[i][i]=1;while(b){if(b%2==1)ans=multi(ans,u,m,m,m);u=multi(u,u,m,m,m);b/=2;}return ans;
}
int p[25];//前缀为i位加一位能转移到j位 方案数
char s[30];
void kmp()
{int len=strlen(s+1);p[1]=0;int j;for(int i=2;i<=len;i++){j=p[i-1];while(s[j+1]!=s[i] && j>0)j=p[j];if(s[i]==s[j+1])j++;p[i]=j;}for(int i=0;i<len;i++)for(int y=0;y<=9;y++){int j=i;while(j>0 && s[j+1]-'0'!=y)j=p[j];if(s[j+1]-'0'==y)j++;st.m[j][i]=(st.m[j][i]+1)%Mod;}
}
int main()
{scanf("%d%d%d",&n,&m,&Mod);scanf("%s",s+1);kmp();st=pow_mod(st,n);int ans=0;for(int i=0;i<m;i++)ans=(ans+st.m[i][0])%Mod;printf("%d\n",ans);return 0;
}