@AkamemakA
2022-07-13T12:49:10.000000Z
字数 2233
阅读 204
题解
杂题
算法
题目链接:点我
给定一个长度为的由数字组成的字符串,要求构造一个长度为的由数字构成的字符串,使得中不包含。
输出方案数的值。
输入和字符串。
4 3 100
111
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;
} //kmp
f[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;
} //kmp
for(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;
}
完结撒花~