HDU 5069 Harry And Biological Teacher(fail树+线段树优化)

题意:
有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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值