[关闭]
@AkamemakA 2022-07-13T12:49:10.000000Z 字数 2233 阅读 204

题解 杂题 算法

Acwing P1035 (HNOI2008) GT考试


题目链接:点我

题目大意

给定一个长度为的由数字组成的字符串,要求构造一个长度为的由数字构成的字符串,使得中不包含
输出方案数的值。

输入和字符串

样例输入

  1. 4 3 100
  2. 111

样例输出

  1. 81

数据范围


解析

做这道题前,我们可以先看一下前置题这里

两道题唯一不同的地方就是,这道题的范围扩大到了,比原先大了不少。

我们可以先看看时的思路:

这里给出代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=55,mod=1e9+7;
  4. int n,m;
  5. char str[N];
  6. int ne[N];
  7. int f[N][N];
  8. int main()
  9. {
  10. cin>>n;
  11. cin>> str+1;
  12. m=strlen(str+1);
  13. for(int i=2,j=0;i<=m;i++){
  14. while(j&&str[j+1]!=str[i]) j=ne[j];
  15. if(str[j+1]==str[i]) j++;
  16. ne[i]=j;
  17. } //kmp
  18. f[0][0]=1;
  19. for(int i=0;i<n;i++)//枚举每个点
  20. for(int j=0;j<m;j++)//枚举最大后缀匹配长度
  21. for(int c='a';c<='z';c++){//枚举要添加的字符
  22. int k=j;
  23. while(k&&str[k+1]!=c) k=ne[k]; //如果添加后不匹配,则重新找到最大的匹配长度
  24. if(str[k+1]==c) k++;
  25. if(k<m) f[i+1][k]=(f[i+1][k]+f[i][j])%mod;
  26. //这里k必须小于m,表示组成的新字符串中不能包含长度为m的字符串S
  27. }
  28. int ans=0;
  29. for(int i=0;i<m;i++) ans=(ans+f[n][i])%mod; //统计答案
  30. cout<<ans;
  31. }

然后我们回头来看这道题。

对于,再枚举每个点肯定不现实。

于是,我们考虑一个两层状态之间的一个转换,即之间的转换:



其中表示对于上述代码的25行要执行的次数。

然后不难发现,所有的可以组成一个矩阵。于是,转移方程就发生了变化:


所以答案
矩阵乘法可以轻松解决。

对于矩阵,在上面代码的状态转移部分,我们可以预处理出。(具体见代码,还可以自己画图分析分析)。


代码实现

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=25;
  4. int n,m,mod;
  5. char str[N];
  6. int ne[N];
  7. int a[N][N];
  8. inline void mul(int c[N][N],int a[N][N],int b[N][N]){
  9. static int t[N][N];
  10. memset(t,0,sizeof t);
  11. for(int i=0;i<m;i++)
  12. for(int j=0;j<m;j++)
  13. for(int k=0;k<m;k++)
  14. t[i][j]=(t[i][j]+a[i][k]*b[k][j])%mod;
  15. memcpy(c,t,sizeof t);
  16. }//矩阵乘法模板
  17. int qpow(int k){
  18. int f[N][N]={1}; //这里写二维数组只是为了方便计算。实际上在计算时,f数组中只有f[0]这一数组在使用,其他都没有用处。
  19. while(k){
  20. if(k&1) mul(f,f,a);
  21. mul(a,a,a);
  22. k>>=1;
  23. }
  24. int res=0;
  25. for(int i=0;i<m;i++)res=(res+f[0][i])%mod; //计算答案
  26. return res;
  27. }
  28. int main()
  29. {
  30. cin>>n>>m>>mod;
  31. cin>> str+1;
  32. for(int i=2,j=0;i<=m;i++){
  33. while(j&&str[j+1]!=str[i]) j=ne[j];
  34. if(str[j+1]==str[i]) j++;
  35. ne[i]=j;
  36. } //kmp
  37. for(int j=0;j<m;j++)
  38. for(int c='0';c<='9';c++){
  39. int k=j;
  40. while(k&&str[k+1]!=c) k=ne[k];
  41. if(str[k+1]==c) k++;
  42. if(k<m)a[j][k]++; //将上面代码中状态转移部分改为预处理,可求出矩阵A
  43. }
  44. cout<<qpow(n); //矩阵快速幂
  45. return 0;
  46. }

完结撒花~

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注