zoukankan      html  css  js  c++  java
  • BZOJ.2780.[SPOJ8093]Sevenk Love Oimaster(广义后缀自动机)

    题目链接

    (Description)

    给定n个模式串,多次询问一个串在多少个模式串中出现过。(字符集为26个小写字母)

    (Solution)

    对每个询问串进行匹配最终会达到一个节点,我们需要得到这个节点所代表的子串出现在多少个模式串中。
    建立广义后缀自动机。每次插入一个串,从root开始,对于SAM上每个节点维护cnt和bef,分别表示该节点代表的串出现在几个模式串中 和 该节点最近被哪个模式串更新过cnt。
    对于bef[i]!=now的节点,++cnt[i],bef[i]=now;当模式串now下次匹配到当前节点时则不再更新。
    另外,如果匹配了当前节点i那么一定会匹配上fa[i],fa[fa[i]]...如果它们的bef[]!=now,则都更新一遍。直到有个节点p满足bef[p]==now,那么就不需要再向上更新了(再往上已经更新过了)。(这个在insert后用np更新就可以啊)

    这个暴力跳的复杂度可能是(O(nsqrt n))的,但是很难卡满(广义SAM上一个点暴力跳fa的次数是(O(sqrt n))的,具体见这里)。
    有一种离线+DFS序+树状数组的做法可以做到(log n)。(注意广义SAM应该要像这题那么建)
    但事实上这题还有个剪枝(bef[np]==now),广义SAM上情况比较复杂我也不知道真正的复杂度是啥。。

    注意新建nq时 bef[nq],cnt[nq]也要复制(=...[q])。

    //24612kb	76ms
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define gc() getchar()
    const int N=2e5+5;
    
    struct Suffix_Automaton
    {
    	int las,tot,fa[N],son[N][26],len[N],cnt[N],bef[N];
    	char s[360005];
    
    	void Init(){
    		las=tot=1;
    	}
    	void Insert(int now,int c)
    	{
    		int p=las,np=++tot; len[las=np]=len[p]+1;
    		for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np;
    		if(!p) fa[np]=1;
    		else
    		{
    			int q=son[p][c];
    			if(len[q]==len[p]+1) fa[np]=q;
    			else
    			{
    				int nq=++tot;
    				len[nq]=len[p]+1, bef[nq]=bef[q], cnt[nq]=cnt[q];//!
    				memcpy(son[nq],son[q],sizeof son[q]);
    				fa[nq]=fa[q], fa[q]=fa[np]=nq;
    				for(; son[p][c]==q; p=fa[p]) son[p][c]=nq;
    			}
    		}
    		for(; bef[np]!=now&&np; np=fa[np])
    			++cnt[np], bef[np]=now;
    	}
    	void Build(int now)
    	{
    		las=1, scanf("%s",s);
    		for(int i=0,l=strlen(s); i<l; ++i)
    			Insert(now,s[i]-'a');
    	}
    	void Query()
    	{
    		int p=1; scanf("%s",s);
    		for(int i=0,l=strlen(s); i<l&&p; ++i)
    			p=son[p][s[i]-'a'];
    		printf("%d
    ",cnt[p]);
    	}
    }sam;
    
    int main()
    {
    	int n,Q; scanf("%d%d",&n,&Q); sam.Init();
    	for(int i=1; i<=n; ++i) sam.Build(i);
    	while(Q--) sam.Query();
    
    	return 0;
    }
    
  • 相关阅读:
    Java虚拟机(第二版) 学习笔记之Class类文件的结构
    JVM之深入浅出之垃圾收集算法
    Java虚拟机(第二版) 学习笔记之OutOfMemoryError
    Java虚拟机(第二版) 学习笔记
    平滑加权轮询负载均衡(轮询)算法
    java AQS(AbstractQueuedSynchronizer)同步器详解
    mybatis Interceptor拦截器代码详解
    aspectj编程简介
    Java并发编程阅读笔记-Java监视器模式示例
    我们该怎么结合日志做优化
  • 原文地址:https://www.cnblogs.com/SovietPower/p/9240586.html
Copyright © 2011-2022 走看看