题意:
有n个字符串(n<=100000),总长度小于等于1e5,有m次查询,X,Y,问第X个的后缀和第Y个的前缀的最长公共子串
思路一:
总体思路
构造fail树,dfs序
按Y排序
X最后一个对应在字典树中的位置首先,我们可以找到a在自动机上所对应的节点,考虑ac自动机的fail指针,那么从这个节点,往上一直到根的链上,
所有的节点所包含的子串,都是X的后缀。故而,我们只需要看这个链上有没有Y的前缀,
如果有,最长是多少。那么我们来看Y的所有的前缀,首先,我们可以找到所有的Y的前缀在自动机上对应的节点,
对于Y的长度为j的前缀,令它对应在ac自动机上的节点为v,那么在dfs 树中,v的子树下所有的节点都有可能以j为答案。
这里就用到线段树的成段更新,单点求最大值就好了(相当于区间赋值)。 整体复杂度为O(nlogn)线段树还有一个时间累加
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+100;
struct Edge{
int to,next;
}e[N];
int tot,head[N],n,m,Len[N],ch[1000],ans[N];
struct node{
int x,y,id;
}a[N];
vector<int>G[N];
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int from,int to){
e[tot]=(Edge){to,head[from]};
head[from]=tot++;
}
int LL,RR,v,pos;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int setv[N*4],maxv[N*4];
void pushdown(int rt){
if(setv[rt]!=0){
setv[rt<<1]=setv[rt<<1|1]=maxv[rt<<1]=maxv[rt<<1|1]=setv[rt];
setv[rt]=0;
}
}
void update(int l,int r,int rt){
if(LL<=l&&RR>=r){
maxv[rt]=setv[rt]=v;
return ;
}
pushdown(rt);
int m=(l+r)>>1;
if(LL<=m) update(lson);
if(RR>m) update(rson);
}
int query(int l,int r,int rt){
if(l==r)
return maxv[rt];
pushdown(rt);
int m=(l+r)>>1;
if(pos<=m) return query(lson);
else return query(rson);
}
struct Trie{
int sz,next[N][4],root,fail[N],TOT,L[N],R[N],Q[N];
int newnode(){
memset(next[sz],-1,sizeof(next[sz]));
sz++;
return sz-1;
}
void init(){
sz=0,TOT=0;
root=newnode();
}
void insert(char s[],int id){
int now=root,len=strlen(s);
for(int i=0;i<len;i++){
if(next[now][ch[s[i]]]==-1)
next[now][ch[s[i]]]=newnode();
now=next[now][ch[s[i]]];
G[id].push_back(now);
}
}
void build(){
int head=0,tail=0;
fail[root]=root;
for(int i=0;i<4;i++)
if(next[root][i]!=-1)
fail[next[root][i]]=root,Q[++tail]=next[root][i],addedge(root,next[root][i]);
while(head<tail){
int now=Q[++head];
for(int i=0;i<4;i++)
if(next[now][i]!=-1){
int k=fail[now];
while(k!=root&&next[k][i]==-1) k=fail[k];
if(next[k][i]!=-1)
k=next[k][i];
fail[next[now][i]]=k;
Q[++tail]=next[now][i];
addedge(k,next[now][i]);
}
}
}
void dfs(int u){
L[u]=++TOT;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
dfs(v);
}
R[u]=TOT;
}
void solve(){
int now,base=0,SZ;
for(int i=1;i<=m;i++){
if(a[i].y!=a[i-1].y){
base+=G[a[i-1].y].size()+1;
SZ=G[a[i].y].size();
for(int j=0;j<SZ;j++){
LL=L[G[a[i].y][j]],RR=R[G[a[i].y][j]],v=base+j+1;
update(1,sz,1);
}
}
pos=L[G[a[i].x][ G[a[i].x].size()-1 ]]; //最后一个元素对应线段树中的位置
ans[a[i].id]=max(0,query(1,sz,1)-base);
}
}
};
Trie ac;
bool cmp(const node& u,const node& v){
if(u.y!=v.y) return u.y<v.y;
return u.x<v.x;
}
char s[N];
int main(){
ch['A']=0,ch['T']=1,ch['C']=2,ch['G']=3;
while(scanf("%d%d",&n,&m)!=EOF){
ac.init();
init();
Len[0]=0;
for(int i=1;i<=n;i++){
G[i].clear();
scanf("%s",s);
ac.insert(s,i);
}
ac.build();
ac.dfs(0);
memset(setv,0,sizeof(setv));
memset(maxv,0,sizeof(maxv));
for(int i=1;i<=m;i++) scanf("%d%d",&a[i].x,&a[i].y),a[i].id=i;
sort(a+1,a+m+1,cmp);
ac.solve();
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
return 0;
}
思路二:
构造fail树,dfs序
查询按X排序(后缀)
X最后一个对应在字典树中的位置
暴力
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+100;
struct Edge{
int to,next;
}e[N];
int tot,head[N],n,m,Len[N],ch[1000],ans[N];
char s[N];
struct node{
int x,y,id;
}a[N];
vector<int>G[N];
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int from,int to){
e[tot]=(Edge){to,head[from]};
head[from]=tot++;
}
struct Trie{
int sz,next[N][4],root,fail[N],TOT,L[N],R[N],Q[N];
int newnode(){
memset(next[sz],-1,sizeof(next[sz]));
sz++;
return sz-1;
}
void init(){
sz=0,TOT=0;
root=newnode();
}
void insert(int st,int ed,int id){
int now=root;
for(int i=st;i<ed;i++){
if(next[now][ch[s[i]]]==-1)
next[now][ch[s[i]]]=newnode();
now=next[now][ch[s[i]]];
G[id].push_back(now);
}
}
void build(){
int head=0,tail=0;
fail[root]=root;
for(int i=0;i<4;i++)
if(next[root][i]!=-1)
fail[next[root][i]]=root,Q[++tail]=next[root][i],addedge(root,next[root][i]);
while(head<tail){
int now=Q[++head];
for(int i=0;i<4;i++)
if(next[now][i]!=-1){
int k=fail[now];
while(k!=root&&next[k][i]==-1) k=fail[k];
if(next[k][i]!=-1)
k=next[k][i];
fail[next[now][i]]=k;
Q[++tail]=next[now][i];
addedge(k,next[now][i]);
}
}
}
void dfs(int u){
L[u]=++TOT;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
dfs(v);
}
R[u]=TOT;
}
void solve(){
int now;
for(int i=1;i<=m;i++){
if(a[i].x!=a[i-1].x)
now=G[a[i].x][G[a[i].x].size()-1];
ans[a[i].id]=0;
for(int j=G[a[i].y].size()-1;j>=0;j--){
int tmp_id=G[a[i].y][j];
if(L[tmp_id]<=L[now]&&R[tmp_id]>=L[now]){ans[a[i].id]=j+1;break;}
}
}
}
};
Trie ac;
bool cmp(const node& u,const node& v){
if(u.x!=v.x) return u.x<v.x;
return u.y<v.y;
}
int main(){
ch['A']=0,ch['T']=1,ch['C']=2,ch['G']=3;
while(scanf("%d%d",&n,&m)!=EOF){
ac.init();
init();
Len[0]=0;
for(int i=1;i<=n;i++){
G[i].clear();
scanf("%s",s);
int len=strlen(s);
ac.insert(0,len,i);
}
ac.build();
ac.dfs(0);
for(int i=1;i<=m;i++) scanf("%d%d",&a[i].x,&a[i].y),a[i].id=i;
sort(a+1,a+m+1,cmp);
ac.solve();
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}
return 0;
}