P3193 [HNOI2008]GT考试 题解
题目大意
略
思路分析
字符串好难嘤嘤嘤
首先考虑暴力就是去直接枚举有多少种号码,然后判断是否不合法,但这样太智障了。
考虑 dp ,发现 m 其实范围是很小的,是我们可以承受的,那么我们可以尝试从这里入手。
设立 \(\text{f[i][j]}\) 表示到第 \(i\) 位时匹配了 \(j\) 个字符的个数。
那么答案就是 \(\sum_{i=0}^{m-1} f[n][i]\) ,那么先不考虑去爆不爆空间。
我们尝试着去写出这个 dp 的转移式子。
\[f[i][j]=\sum_{k=0}^{m-1} f_{i-1,k} \times g_{k,j}
\]
先把式子摆出来。
其中的 \(g_{k,j}\) 表示一个匹配了长度为 \(k\) 长度的串,有多少种加数字的方法,使得匹配长度变成 \(j\)。
由于我们知道原串,那么 \(g_{j,k}\) 是固定不变的。
我们可以使用 KMP算法,枚举匹配长度 \(k\) 和字符 ,暴力计算能匹配到多长的前缀。
然后复杂度为 \(nm^2\)
仔细观察发现这是一个经典矩乘的样子。
那么直接预处理矩阵然后矩阵快速幂即可。
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e3,M = 30;
int n,m,mod,tp[N];
char s[N];
struct Martix{
int a[30][30];
Martix(){memset(a,0,sizeof(a));}
Martix operator * (const Martix &B)const{
Martix res;
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
for(int k=0;k<m;k++){
res.a[i][j]=(res.a[i][j]+a[i][k]*B.a[k][j])%mod;
}
}
}
return res;
}
}G,F;
void build(char *s,int len,int *fail){
int p=0;
for(int i=2;i<=len;i++){
while(p&&s[p+1]!=s[i]) p=fail[p];
if(s[p+1]==s[i]) p++;
fail[i]=p;
}
for(int i=0;i<len;i++){
for(char ch='0';ch<='9';ch++){
p=i;
while(p&&s[p+1]!=ch) p=fail[p];
if(s[p+1]==ch) ++p;
++G.a[i][p];
}
}
}
Martix power(Martix &a,int b){
Martix ans;
for(int i=0;i<m;i++) ans.a[i][i]=1;
while(b){
if(b&1) ans=ans*a;
b>>=1;
a=a*a;
}
return ans;
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&mod);
scanf("%s",s+1);
build(s,m,tp);
G=power(G,n);
int ans=0;
for(int i=0;i<m;i++) ans=(ans+G.a[0][i])%mod;
printf("%lld\n",ans);
return 0;
}

浙公网安备 33010602011771号