CF86C Genetic engineering 题解
DP 套 DP 模板。
题意:给定 \(n\) 个长度小于等于 \(10\) 的模式串,询问有多少个长为 \(m\) 的文本串满足要求。要求为,该文本串可以被若干个模式串完全覆盖,可以重叠。\(n \le 10,m\le 1000\)。
建出 AC 自动机是明显的。套路地,要设 DP 状态的前两维为 \(i,j\) 表示当前串长度为 \(i\),位于自动机节点 \(j\),但是这好像不能统计是否满足要求,那就考虑再加一维 \(k\),表示前 \(k\) 位已经满足要求。
建树时把结尾节点的 \(val\) 记为该模式串长度,则 DP 到一个节点时,它能向前覆盖的长度就是其在 \(fail\) 树上到根的路径上 \(val\) 的最大值。那么在 DP 时,只要 \(i+1-val \le k\),我们就能把这一位更新到 \(i+1\),否则保留 \(k\)。
时间复杂度 \(O(m^2\sum|s|)\),其实可以过。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m;
const int N = 1e2+10,mod=1e9+9;
string t,s;
map<char,int>mp;
int tr[N][4],fail[N],val[N],tot;
int f[2][N][1005];
void ins(string s){
int p=0;
for(int i=0;i<s.size();i++){
int c=mp[s[i]];
if(!tr[p][c])tr[p][c]=++tot;
p=tr[p][c];
}
val[p]=s.size();
}
void build(){
queue<int>q;
for(int i=0;i<4;i++){
if(tr[0][i]){
q.push(tr[0][i]);
}
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<4;i++){
if(tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
val[tr[u][i]]=max(val[tr[u][i]],val[fail[tr[u][i]]]);
q.push(tr[u][i]);
}else{
tr[u][i]=tr[fail[u]][i];
}
}
}
}
inline int pls(int x,int y){
return ((x+y>=mod)?(x+y-mod):(x+y));
}
inline void add(int &x,int y){
x=pls(x,y);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>m>>n;
mp['A']=0,mp['T']=1,mp['G']=2,mp['C']=3;
for(int i=1;i<=n;i++){
cin>>t;
ins(t);
}
build();
f[0][0][0]=1;
for(int i=0;i<m;i++){
for(int j=0;j<=tot;j++){
for(int k=0;k<=m;k++){
f[(i+1)&1][j][k]=0;
}
}
for(int j=0;j<=tot;j++){
for(int k=0;k<=i;k++){
for(int c=0;c<4;c++){
int lst=i+1-val[tr[j][c]];
add(f[(i+1)&1][tr[j][c]][(lst<=k?i+1:k)],f[i&1][j][k]);
}
}
}
}
int ans=0;
for(int i=0;i<=tot;i++){
add(ans,f[m&1][i][m]);
}
cout<<ans;
return 0;
}
但其实可以优化,因为如果 \(i\) 和 \(k\) 相差超过了最长的模式串长度的时候,就必然无解了。那就可以只枚举在有解范围内的 \(k\),时间复杂度 \(O(mL\sum|s|)\),其中 \(L\) 是最长的模式串长度。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define _int __int128
#define ull unsigned long long
#define pii pair<int,int>
#define fst first
#define scd second
#define pq priority_queue
#define mkp make_pair
#define popcount(x) __builtin_popcount(x)
#define endl '\n'
int n,m,len;
const int N = 1e2+10,mod=1e9+9;
string t,s;
map<char,int>mp;
int tr[N][4],fail[N],val[N],tot;
int f[2][N][1005];
void ins(string s){
int p=0;
for(int i=0;i<s.size();i++){
int c=mp[s[i]];
if(!tr[p][c])tr[p][c]=++tot;
p=tr[p][c];
}
val[p]=s.size();
}
void build(){
queue<int>q;
for(int i=0;i<4;i++){
if(tr[0][i]){
q.push(tr[0][i]);
}
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=0;i<4;i++){
if(tr[u][i]){
fail[tr[u][i]]=tr[fail[u]][i];
val[tr[u][i]]=max(val[tr[u][i]],val[fail[tr[u][i]]]);
q.push(tr[u][i]);
}else{
tr[u][i]=tr[fail[u]][i];
}
}
}
}
inline int pls(int x,int y){
return ((x+y>=mod)?(x+y-mod):(x+y));
}
inline void add(int &x,int y){
x=pls(x,y);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>m>>n;
mp['A']=0,mp['T']=1,mp['G']=2,mp['C']=3;
for(int i=1;i<=n;i++){
cin>>t;
len=max(len,(int)t.size());
ins(t);
}
build();
f[0][0][0]=1;
for(int i=0;i<m;i++){
for(int j=0;j<=tot;j++){
for(int k=0;k<=m;k++){
f[(i+1)&1][j][k]=0;
}
}
for(int j=0;j<=tot;j++){
for(int k=0;k<=i;k++){
if(i+1-k>len)continue;
for(int c=0;c<4;c++){
int lst=i+1-val[tr[j][c]];
add(f[(i+1)&1][tr[j][c]][(lst<=k?i+1:k)],f[i&1][j][k]);
}
}
}
}
int ans=0;
for(int i=0;i<=tot;i++){
add(ans,f[m&1][i][m]);
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号