这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题。

  这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合适,这道题在构建自动机时不能改变原有的 Trie树 的结构,所以没有代表字符串的结点的不需要去改它的值,所以我在 build() 函数 处做了一些修改。在复杂度方面,如果是强上普通的AC自动机,最差会达到O(n^2),感觉不太好。我们这里可以用到平方分割的套路,搞大小两个自动机,每次插入都在小自动机上进行,当小自动机里的结点达到一定量(sqrt(n))时,就将两自动机合并,并将小自动机清空。合并的方式也很好理解,就将两个自动机的节点一一对应,若小自动机上的结点在大自动机上没有,就在大自动机上新建结点,并修改它的值。总复杂度大概是这样:O(n*sqrt(n)(小的)+O(sqrt(n)*n)(大的)=O(n*sqrt(n))。其实那个上界也不一定要严格的根号n,自己大概估计一个常数就差不多了。哦对,还有一点,我发现好多板子里在构建新结点时都打了一个 newnode() 函数,包括我自己的板子也是这么打的。这样打其实是很慢的,我在做这道题时就各种 T飞 ,后来把这个去掉搞成其他的方法就快了很多(虽然我也不知道为什么),下面的代码里会体现。

  祝大家切题愉快

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#define maxn (511111)
#define N (6000000)
#define il inline
#define ll long long
#define RG register
using namespace std;
char s[N],ss[N];

struct Trie{
	int son[maxn][2],fail[maxn],size,root;
	bool val[maxn];     //标记是否出现
	il void init(){
		size=1;	root=0;
		memset(son,0,sizeof(son));
		memset(val,0,sizeof(val));
		memset(fail,0,sizeof(fail));
	}

	il int idx(char c){  //卡常专用
		return c-'0';
	}

	il int insert(char *s){     //插入新单词
		RG int cur=root;
		for(RG int i=0;s[i];i++){   //卡常小技巧,不要每次计算len
			RG int id=idx(s[i]);
			if( !son[cur][id] ) son[cur][id]=size++;
			cur=son[cur][id];
		}
		val[cur]=true;
		return cur;
	}

	il bool in(char *s){        //查询单词是否在自动机内
		RG int cur=root;
		for(RG int i=0;s[i];i++){
			RG int id=idx(s[i]);
			if( !son[cur][id] ) return false;
			cur=son[cur][id];
		}
		return val[cur];
	}

	il void build(){        //构建自动机
		queue<int>Q;
		for(RG int i=0;i<2;i++)
			if( son[root][i] )       //只加入队列,不用修改值
       	Q.push(son[root][i]);

		while(!Q.empty()){
			RG int cur=Q.front(); Q.pop();
			for(RG int i=0;i<2;i++){
				RG int Son=son[cur][i];
				if(!Son) continue;
				Q.push(Son);         //同上
				RG int f=fail[cur];
				while(f && !son[f][i] ) f=fail[f];
				fail[Son]=son[f][i];
			}
		}
	}

	il int query(char *s){     //查询次数
		RG int cur=root,ans=0;
		for(RG int i=0;s[i];i++){
			RG int id=idx(s[i]);
			while(cur && !son[cur][id] ) cur=fail[cur];
			cur=son[cur][id];
			RG int k=cur;
			while(k){
				ans+=val[k];
				k=fail[k];
			}
		}
		return ans;
	}
}small,big;

il void dfs(RG int u,RG int v){        //用于合并
	for(RG int i=0;i<2;i++)
		if(small.son[v][i]){
			RG int cur2=small.son[v][i];
			if( !big.son[u][i] ){
				memset(big.son[big.size],0,sizeof(big.son[big.size]));
				big.son[u][i]=big.size++;       //建立新结点
			}
			RG int cur1=big.son[u][i];
			big.val[cur1] |=small.val[cur2];
			dfs(cur1,cur2);
		}
}

il void join(){
	dfs(0,0);
	small.init();
	big.build();
}

int main(){
	RG int Case,n;
	scanf("%d",&Case);
	for(RG int k=1;k<=Case;k++){
		printf("Case #%d:\n",k);
		scanf("%d",&n);
		small.init();  big.init();
		RG int l=0;
		while(n--){
			scanf("%s",s);
			RG int len=strlen(s+1);
			ss[0]=s[0];
			for(RG int i=0;i<len;i++)
				ss[i+1]=s[ (i+l%len+len)%len+1 ];
			ss[len+1]='\0';
			if(ss[0]=='+'){
                      if( small.in(ss+1) || big.in(ss+1) ) continue;
                      //若该单词已经存在就跳过
				small.insert(ss+1);  small.build();
				if( small.size>2333 ) join();
			}
			else{
				l=small.query(ss+1)+big.query(ss+1);
				printf("%d\n",l);
			}
		}
	}
	return 0;
}

  真是服了这鬼畜的缩进。。。真难看。。。

HDU-4787 GRE Words Revenge 解题报告的更多相关文章

  1. [HDU 4787] GRE Words Revenge (AC自动机)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4787 题目大意: 给你若干个单词,查询一篇文章里出现的单词数.. 就是被我水过去的...暴力重建AC自 ...

  2. ●HDU 4787 GRE Words Revenge

    题链: http://acm.hdu.edu.cn/showproblem.php?pid=4787 题解: AC自动机(强制在线构造) 题目大意: 有两种操作, 一种为:+S,表示增加模式串S, 另 ...

  3. HDU 4787 GRE Words Revenge

    Description Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. ...

  4. [HDU4787]GRE Words Revenge 解题报告

    这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题. 这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合 ...

  5. hdu 1556.Color the ball 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1556 题目意思:有 n 个气球从左到右排成一排,编号依次为1,2,3,...,n.给出 n 对 a, ...

  6. hdu 1160 FatMouse's Speed 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1160 题目意思:给出一堆老鼠,假设有 n 只(输入n条信息后Ctrl+Z).每只老鼠有对应的weigh ...

  7. hdu 1879 继续畅通工程 解题报告

    题目链接:http://code.hdu.edu.cn/showproblem.php?pid=1879 这条题目我的做法与解决Constructing Roads的解法是相同的. 0 表示没有连通: ...

  8. hdu 1233 还是畅通工程 解题报告

    题目链接:http://code.hdu.edu.cn/showproblem.php?pid=1233 并查集的运用, 实质就是求最小生成树.先对所有的村庄距离从小到大排序,然后判断村庄之间是否属于 ...

  9. hdu 1213 How Many Tables 解题报告

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 有关系(直接或间接均可)的人就坐在一张桌子,我们要统计的是最少需要的桌子数. 并查集的入门题,什 ...

随机推荐

  1. CSS3的使用方法解析

    自己过去有段时间使用CSS3开发过一些小的部件和效果,但是由于太久没有再次去使用,导致当自己再次去使用的时候我就需要去翻手册重新找一次然后按着方法使用才可以. 现在我就把这份CSS3的使用技巧展示给各 ...

  2. Safari Private 模式下 localStorage 的问题

    现如今好多浏览器都有「隐身模式」,Safari 管这叫「Private Browing」,国内各种牌子的套壳浏览器叫「无痕浏览」.私以为从命名上来说,倒是国内更中文一些. 这种模式下浏览网页踏雪无痕, ...

  3. vue2.0实现分页组件

    最近使用vue2.0重构项目, 需要实现一个分页的表格, 没有找到合适的组件, 就自己写了一个, 效果如下: 该项目是使用 vue-cli搭建的, 如果你的项目中没有使用webpack,请根据代码自己 ...

  4. js中的confirm的运用

    <a href="javascript:void(0);" onclick='tuichu_queren();'>安全退出</a> <script t ...

  5. AngularJs学习笔记1——总体介绍

    这周末在家呆了两天,正好中午闲暇时间继续分享Angularjs相关,今天主要分享Angularjs总体介绍及数据绑定部分内容,下面直接进入主题. 1.基本概念: AngularJS是为了克服HTML在 ...

  6. Agile&DevOps究竟谁是魔法棒

    天下没有神奇的配方 很抱歉,文章的开头我就要说出这个残酷的事实 - 世界上没有任何工具可以魔法般的让你实现敏捷,精益,DevOps.如果只是依赖了工具的自动化,实现了自动化Dev或者Ops,那么别忘了 ...

  7. Apollo框架试玩

    2017年7月5日,百度举行了AI开发者大会,在会上发布了Apollo项目,并进行了演示,该项目在Github上已经能够被访问.出于一个程序员的好奇,昨天试玩了一把,确实不错. http://apol ...

  8. Java虚拟机面试重点-------------内存分配和回收策略

    1 对象优先分配在Eden区 对象优先在Eden进行分配,大多数情况下,对象在新生代Eden区进行分配.当Eden区没有足够的空间进行分配时,虚拟机会发起一次Minor GC. 新生代GC(Ninor ...

  9. 流行框架(angularj基础)

  10. Chrome浏览器扩展开发系列之十四:本地消息机制Native messaging

    通过将浏览器所在客户端的本地应用注册为Chrome浏览器扩展的“本地消息主机(native messaging host)”,Chrome浏览器扩展还可以与客户端本地应用之间收发消息. 客户端的本地应 ...