基因切割
题目概述
题解
由于
N
O
I
=
I
O
N
=
I
n
s
t
i
t
u
t
e
?
o
f
?
N
a
v
i
g
a
t
i
o
n
=
航
海
协
会
NOI=ION=Institute\,of\,Navigation=航海协会
NOI=ION=InstituteofNavigation=航海协会,所以就叫这段时间的模拟赛题航海协会吧。
我们可以将题目稍微转化一下,很容易发现他那种复杂的匹配方法可以等价于依次对于
i
=
1
,
.
.
.
,
m
i=1,...,m
i=1,...,m暴力找到整个串中的
T
i
T_i
Ti?,将它们删掉。 那么显然就可以
O
(
∑
∣
T
∣
2
m
)
O\left(\sum |T|^2m\right)
O(∑∣T∣2m)直接暴力匹配,再加一个KMP优化一下,就只要
O
(
∑
∣
T
∣
n
)
O\left(\sum |T|n\right)
O(∑∣T∣n)了。
我们想想我们的复杂度是被卡在每一次都要跑整个串上,我们能否先将一部分答案用其他的方式处理出来。 我们不妨先预设一个阈值
B
B
B,对于小于
B
B
B的字符串,我们可以先建立一棵
T
r
i
e
Trie
Trie树,然后对于每个前缀的长度不超过
B
B
B的后缀,放到
T
r
i
e
Trie
Trie树上跑,看能够匹配到的最靠前的询问串是哪一个,放到该询问串的集合中,等到处理这个询问串的时候,就将集合中的串拿出来一个一个删掉。 可以发现,每次我们从原串中删掉一部分,都势必导致一些包含删掉元素的串发生改变。 显然,我们删掉的串是一个连续串,所以只会影响到后面的
B
B
B位,将它们重新找一下即可。 显然,我们每次重新找,都会消耗
O
(
B
3
)
O(B^3)
O(B3)的时间,如果倒过来建Trie树,就可以优化到
B
2
B^2
B2,因为这样就只用找Trie树一条路径上的最小值。 注意,当我们取出一个集合中的元素删掉时也有可能影响这个集合中其它元素的值,所以应该每次取出一个集合中最靠前的串删掉。
至于我们如何维护原串现在还没被删掉的位置,我们可以考虑使用链表的结构,将每个被删掉的区间连在一起,因为我们在串上跳每一步也最多只会经过一个被完全删掉的区间,直接将它跳过即可。 而询问串是可能相同的,所以我们可以用内设前向星的方式维护Trie树上每个节点的对应值,当这个询问串被操作后,就跳到下一个询问串上。 注意一下,同一个询问串导致删掉的区间是不能包含的。
时间复杂度
O
(
n
2
B
+
n
B
2
?
n
2
3
)
O\left(\frac{n^2}{B}+nB^2\geqslant n^{\frac{2}{3}}\right)
O(Bn2?+nB2?n32?),当
B
B
B取
n
1
3
n^{\frac{1}{3}}
n31?时最优。 跑得超级快,至少比
b
i
t
s
e
t
bitset
bitset做法快
3
3
3倍。
源码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
#define MAXN 110005
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lowbit(x) (x&-x)
#define lson (rt<<1)
#define rson (rt<<1|1)
const int n1=50;
const int INF=0x3f3f3f3f;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1;}return t;}
char str[MAXN],ttr[MAXN];bool vis[MAXN];
int m,n,bg[MAXN],ed[MAXN],len[MAXN],a[MAXN],b[MAXN],nxt[MAXN],pos[MAXN];
int stak,sta[MAXN],belong[MAXN],t[MAXN],stb[MAXN],stk,tt[MAXN],tp,L[MAXN],R[MAXN];
set<int>st[MAXN];
int turn(char x){
if(x=='A')return 0;
if(x=='C')return 1;
if(x=='G')return 2;
return 3;
}
struct ming{
int ch[5],val;
ming(){for(int i=0;i<4;i++)ch[i]=0;val=0;}
};
class TrieTree{
private:
int tot,root;ming tr[MAXN];
public:
void init(){root=++tot;}
void insert(int id){
int now=root;
for(int i=1;i<=stak;i++){
if(!tr[now].ch[sta[i]])
tr[now].ch[sta[i]]=++tot;
now=tr[now].ch[sta[i]];
}
pos[id]=now;nxt[id]=tr[now].val;tr[now].val=id;
}
int query(int dn){
int now=root,res=INF;
for(int i=1;i<=stak;i++){
now=tr[now].ch[sta[i]];if(!now)break;
if(tr[now].val>=dn)res=min(res,tr[now].val);
else if(nxt[tr[now].val]>=dn)res=min(res,nxt[tr[now].val]);
}
if(res>INF-1)return 0;return res;
}
void modify(int x){tr[x].val=nxt[tr[x].val];}
}T;
void insert(int x){
vis[x]=1;L[x]=R[x]=x;
if(vis[x+1])L[R[x+1]]=L[x],R[L[x]]=R[x+1];
if(vis[x-1])L[R[x]]=L[x-1],R[L[x-1]]=R[x];
}
int askpre(int x){return !vis[x-1]?x-1:L[x-1]-1;}
int asksuf(int x){return !vis[x+1]?x+1:R[x+1]+1;}
int main(){
freopen("dna.in","r",stdin);
freopen("dna.out","w",stdout);
scanf("%s",str+1);n=(int)strlen(str+1);read(m);T.init();
for(int i=1;i<=n;i++)a[i]=turn(str[i]);
for(int i=1;i<=m;i++){
bg[i]=ed[i-1]+1;scanf("%s",ttr+bg[i]);
len[i]=(int)strlen(ttr+bg[i]);ed[i]=bg[i]+len[i]-1;
for(int j=bg[i];j<=ed[i];j++)b[j]=turn(ttr[j]);
}
for(int i=m;i>0;i--)if(len[i]<=n1){
for(int j=ed[i];j>=bg[i];j--)sta[++stak]=b[j];
T.insert(i);stak=0;
}
for(int i=1;i<=n;i++){
stak=0;for(int j=i;j>max(0,i-n1);j--)sta[++stak]=a[j];
belong[i]=T.query(1);if(belong[i])st[belong[i]].insert(i);
}
int tot=0;
for(int i=1;i<=m;i++){
if(len[i]<=n1){
while(!st[i].empty()){
int x=*st[i].begin(),l=x,r=asksuf(x);stk=0;
for(int j=1;j<=len[i];j++){
int t=l;l=askpre(l);insert(t);
if(belong[t])st[belong[t]].erase(t);
}
for(int j=1;j<n1&&l;j++)stb[++stk]=l,l=askpre(l);
reverse(stb+1,stb+stk+1);
for(int j=1;j<n1&&r<=n;j++)stb[++stk]=r,r=asksuf(r);
int num=0;tot++;
for(int j=1;j<=stk;j++){
if(stb[j]<=x)continue;int x=stb[j];stak=0;
for(int k=j;k>max(j-n1,0);k--)
sta[++stak]=a[stb[k]];
if(belong[x])st[belong[x]].erase(x);
num++;belong[x]=T.query(i+(num<len[i]));
if(belong[x])st[belong[x]].insert(x);
}
}
T.modify(pos[i]);
}
else{
for(int j=2;j<=len[i];j++){
int now=t[j-1];while(now&&b[now+bg[i]]!=b[j+bg[i]-1])now=t[now];
t[j]=(b[now+bg[i]]==b[j+bg[i]-1])?now+1:0;
}
tp=0;
for(int j=1,las=0;j<=n;j++)if(!vis[j]){
while(las&&b[las+bg[i]]!=a[j])las=t[las];
las=(b[las+bg[i]]==a[j])?las+1:0;tt[++tp]=j;
if(las==len[i]){
int x=j,l=x,r=asksuf(x);stk=las=0;
for(int k=1;k<=len[i];k++){
int t=l;l=askpre(l);insert(t);
if(belong[t])st[belong[t]].erase(t);
}
for(int k=1;k<n1&&l;k++)stb[++stk]=l,l=askpre(l);
reverse(stb+1,stb+stk+1);
for(int k=1;k<n1&&r<=n;k++)stb[++stk]=r,r=asksuf(r);
for(int k=1;k<=stk;k++){
if(stb[k]<=x)continue;int x=stb[k];stak=0;
for(int l=k;l>max(k-n1,0);l--)
sta[++stak]=a[stb[l]];
if(belong[x])st[belong[x]].erase(x);
belong[x]=T.query(i);
if(belong[x])st[belong[x]].insert(x);
}
}
}
}
}
for(int i=1;i<=n;i++)if(!vis[i])printf("%c",str[i]);puts("");
return 0;
}
谢谢!!!
|