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;
}

posted @ 2021-07-28 11:17  Pitiless0514  阅读(125)  评论(0)    收藏  举报