@AkamemakA
2022-07-13T12:49:10.000000Z
字数 2233
阅读 247
题解 杂题 算法
题目链接:点我
给定一个长度为的由数字组成的字符串,要求构造一个长度为的由数字构成的字符串,使得中不包含。
输出方案数的值。
输入和字符串。
4 3 100111
81
解析
做这道题前,我们可以先看一下前置题这里。
两道题唯一不同的地方就是,这道题的范围扩大到了,比原先大了不少。
我们可以先看看时的思路:
于是,我们便得到了状态转移方程:
对于字符串快速匹配,我们又要请出这位大神——算法。它可以快速求出一个数组,表示一个字符串的前位中的最大前后缀匹配,具体原理不作讲解。如,对于字符串,则。
这里给出代码:
#include<bits/stdc++.h>using namespace std;const int N=55,mod=1e9+7;int n,m;char str[N];int ne[N];int f[N][N];int main(){cin>>n;cin>> str+1;m=strlen(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;} //kmpf[0][0]=1;for(int i=0;i<n;i++)//枚举每个点for(int j=0;j<m;j++)//枚举最大后缀匹配长度for(int c='a';c<='z';c++){//枚举要添加的字符int k=j;while(k&&str[k+1]!=c) k=ne[k]; //如果添加后不匹配,则重新找到最大的匹配长度if(str[k+1]==c) k++;if(k<m) f[i+1][k]=(f[i+1][k]+f[i][j])%mod;//这里k必须小于m,表示组成的新字符串中不能包含长度为m的字符串S}int ans=0;for(int i=0;i<m;i++) ans=(ans+f[n][i])%mod; //统计答案cout<<ans;}
然后我们回头来看这道题。
对于,再枚举每个点肯定不现实。
于是,我们考虑一个两层状态之间的一个转换,即之间的转换:
其中表示对于上述代码的25行要执行的次数。
然后不难发现,所有的可以组成一个矩阵。于是,转移方程就发生了变化:
对于矩阵,在上面代码的状态转移部分,我们可以预处理出。(具体见代码,还可以自己画图分析分析)。
#include<bits/stdc++.h>using namespace std;const int N=25;int n,m,mod;char str[N];int ne[N];int a[N][N];inline void mul(int c[N][N],int a[N][N],int b[N][N]){static int t[N][N];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 qpow(int k){int f[N][N]={1}; //这里写二维数组只是为了方便计算。实际上在计算时,f数组中只有f[0]这一数组在使用,其他都没有用处。while(k){if(k&1) mul(f,f,a);mul(a,a,a);k>>=1;}int res=0;for(int i=0;i<m;i++)res=(res+f[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;} //kmpfor(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]++; //将上面代码中状态转移部分改为预处理,可求出矩阵A}cout<<qpow(n); //矩阵快速幂return 0;}
完结撒花~