zoukankan      html  css  js  c++  java
  • 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
    ",ans[i]);
    	}
    	return 0;
    }
    

    复杂度分析:

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

  • 相关阅读:
    微信小程序通过getPhoneNumber后台PHP解密获取用户手机号码
    设置API:wx.openSetting,wx.getSetting使用说明(示例:地图授权与取消授权后的重新授权)
    微信小程序之上传图片和图片预览
    微信小程序上传图片(前端+PHP后端)
    微信小程序拒绝授权后重新拉起授权窗口
    微信小程序如何使用 Git 实现版本管理和协作开发
    配置同时使用 Gitlab、Github、Gitee(码云) 共存的开发环境
    linux 系统下Anaconda的安装【安装python3.6环境首选】
    利用phpqrcode二维码生成类库合成带logo的二维码并且用合成的二维码生成海报(二)
    利用phpqrcode二维码生成类库和imagecopymerge函数制拼接图片的经验(一)
  • 原文地址:https://www.cnblogs.com/zryabc/p/10464271.html
Copyright © 2011-2022 走看看