题解:P8643 [蓝桥杯 2016 国 AC] 碱基
wangkelin123 · · 题解
看到题解里有很多都是爆搜的,但是这并没有什么技术含量。这里我发一个 DP 题解。
题目
这个题面写的很绕,但其实就想表达:有多少个连续子串满足长度为
解法
哈希必不可少,运用前缀和的方法(可参照这题)算出每个字符串中长度为
接下来便是 DP 环节。
第一重循环当然是遍历字符串,下一重就是遍历此字符串中长度为
DP 的方法即为把所有的
最后把所有的
设所有长度为
注意取模!
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll N=1e5+5,base=131,mod=1e9+7;
int n,m,k;
ll ans;
ull pre[N],h[10][N],p[10][N],tot[10];
string s;
map<ull,ll>cnt[10],f[10][10];//f[i][j][H]表示前i个串(以i为结尾),选了j个,哈希值为H的数量
map<ull,bool>vis;
ull Hash(int l,int r,int i){
return h[i][r]-h[i][l-1]*pre[r-l+1];
//123456789 4567->1234567-123*10^4
}
int main(){
cin>>n>>m>>k;
pre[0]=1;
for(int i=1;i<=1e5;i++) pre[i]=pre[i-1]*base;
for(int i=1;i<=n;i++){
cin>>s;
for(int j=1;j<=s.size();j++) h[i][j]=h[i][j-1]*base+(s[j-1]-'A'+1);
vis.clear();
for(int j=1;j+k-1<=s.size();j++){
ull H=Hash(j,j+k-1,i);
cnt[i][H]++;
if(!vis[H]){
p[i][++tot[i]]=H;
vis[H]=true;
}
}
}
for(int i=1;i<=n;i++){
for(int q=1;q<=tot[i];q++){
ull H=p[i][q];
f[i][1][H]=cnt[i][H];
for(int j=1;j<=m;j++){
for(int lst=1;lst<i;lst++){
f[i][j][H]=(f[i][j][H]+f[lst][j-1][H]*cnt[i][H]%mod)%mod;
}
if(j==m) ans=(ans+f[i][j][H])%mod;
}
}
}
cout<<ans;
return 0;
}