HDU5069 Harry And Biological Teacher

题目

As we all know, Harry Porter learns magic at Hogwarts School. However, learning magical knowledge alone is insufficient to become a great magician. Sometimes, Harry also has to gain knowledge from other certain subjects, such as language, mathematics, English, and even algorithm.
Today, Harry is on his biological class, his teacher is doing experiment with DNA right now. But the clever teacher faces a difficult problem. He has lots of genes. Every time he picks gene a and gene b. If he want to connect gene a and gene b, he should calculate the length of longest part that the gene a’s suffix and gene b’s prefix can overlap together. For example gene a is "AAT" and gene b is "ATT", then the longest common part is "AT", so the answer is 2. And can you solve this difficult problem for him?

大意是给你一堆字符串,每次询问两个字符串的前缀和后缀的最大匹配。

思路

显然要用AC自动机做。

我们能用的有两棵树:Trie树和Fail树,一个代表前缀,一个代表后缀,很自然的往两棵树上去考虑。

对于前缀而言,在将字符串插入Trie树的时候,我们同时在它经过的每个节点上加入当前字符串的编号和它到这里时的长度。

离线询问。

然后在fail树上dfs,经过的点就将它上面的信息加入set中(每个字符串建一个set,表示当前字符串前面出现的前缀的值)。然后因为fail树记录的是后缀

,所以从根到某个节点路径上的每一个节点,都是当前点的后缀,这样前缀和后缀就建立匹配了。

因为i,j打错调了一天

代码

#include<bits/stdc++.h>
#define M 100005
#define clr(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,mp[105];
int val[M],ttt[3],h[3][M],ans[M];
set<int>SS[M]; 
struct edge{
    int nxt,to,ex;  
}G[3][M<<1];
void Add(int a,int b,int c,int op){
    G[op][++ttt[op]]=(edge){h[op][a],b,c};
    h[op][a]=ttt[op];   
}
struct AC_automaton{
    int tt,tot;
    int pa[M][5],f[M];
    void clear(){
        clr(pa,0);
        clr(f,0);
        tt=tot=0;
    }
    void Insert(char *S,int d){
        int u=0,l=strlen(S);
        for(int i=0;i<l;i++){
            if(!pa[u][S[i]])pa[u][S[i]]=++tt;
            u=pa[u][S[i]];
            Add(u,d,i+1,1);
        }
        val[d]=u;
    }
    void get_fail(){
        queue<int>Q;
        for(int i=1;i<=4;i++){
            if(pa[0][i]!=0){
                f[pa[0][i]]=0;
                Q.push(pa[0][i]);   
            }
        }
        while(!Q.empty()){
            int u=Q.front();Q.pop();
            for(int i=1;i<=4;i++){
                if(pa[u][i]!=0){
                    f[pa[u][i]]=pa[f[u]][i];
                    Q.push(pa[u][i]);
                }
                else pa[u][i]=pa[f[u]][i];
            }
        }
        for(int i=1;i<=tt;i++)Add(f[i],i,-1,0);//fail树 
    }
}Tr;
char S[M];
void dfs(int x){
    for(int i=h[1][x];i;i=G[1][i].nxt)SS[G[1][i].to].insert(G[1][i].ex);
    for(int i=h[2][x];i;i=G[2][i].nxt){
        if(SS[G[2][i].to].empty())ans[G[2][i].ex]=0;
        else ans[G[2][i].ex]=*(--SS[G[2][i].to].end());
    }
    for(int i=h[0][x];i;i=G[0][i].nxt)dfs(G[0][i].to);
    for(int i=h[1][x];i;i=G[1][i].nxt)SS[G[1][i].to].erase(G[1][i].ex);
}
int main(){
    mp['A']=1;mp['T']=2;mp['C']=3;mp['G']=4;
    while(~scanf("%d%d",&n,&m)){
        Tr.clear();clr(h,0);clr(ttt,0);clr(val,0);
        for(int i=1;i<=n;i++){
            SS[i].clear();
            scanf("%s",S);
            int l=strlen(S);
            for(int j=0;j<l;j++)S[j]=mp[S[j]];
            Tr.Insert(S,i);
        }
        Tr.get_fail();
        for(int i=1,a,b;i<=m;i++){
            scanf("%d%d",&a,&b);
            Add(val[a],b,i,2);
        }
        dfs(0);
        for(int i=1;i<=m;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

复杂度分析:

每个串的每个点,都会进出set一次,而串的总长不超过1e5,所以复杂度是\(O(nlogn)\)的。

全部评论

相关推荐

等闲_:感觉有好多地方会被问穿,mysql存储向量这个方案问题应该很大的,如果深问的的话,为什么不用es,不用pg,不用mivus,分块策略是怎么做的,向量化是怎么向量化的,稠密向量还是稀疏向量,再深问余弦相似度,HSWM算法,Bm25算法,为什么不用混合检索或者Rank重排序优化?其他的项目不停机分库分表咋实现的,切库过程中数据有diff的话有没有补偿策略?既然有了分库分表了有没有碰到业务上不好优化的慢sql,让这个sql读从库?而且点评的话,最好自己压测过,要不这个数据也不好解释。现在就27的情况来看,很多同学已经有了中大厂实习,这个节点也会偏向这些有大厂实习的92同学,而且hc也不多,所以坚持海投吧
听劝,我这个简历该怎么改...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务