这是我之前博客里提到的一道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. 基于SSE实现的极速的矩形核腐蚀和膨胀(最大值和最小值)算法。

    因未测试其他作者的算法时间和效率,本文不敢自称是最快的,但是速度也可以肯定说是相当快的,在一台I5机器上占用单核的资源处理 3000 * 2000的灰度数据用时约 20ms,并且算法和核心的大小是无关 ...

  2. 原生javascript实现网页显示日期时钟效果

    刚接触javascript中Date内置对象时,以为这些方法都太简单了,结果要自己实际操作写一个时钟效果还真一时把我难住了,主要有几点大家要注意的.先看实际效果 要实现这样的效果 某年某月某日星期几几 ...

  3. Ipython 自动重载

    一. 使用示例 In [1]: %load_ext autoreload In [2]: %autoreload 2 # Reload all modules (except those exclud ...

  4. jQuery Ajax封装(附带加载提示和请求结果提示模版)

    1.创建HTML文件(demo) <!doctype html> <html lang="en"> <head> <meta charse ...

  5. Intellij IDEA 2017集成MyBatis三剑客

    MyBatis三剑客指的是:MyBatis-Generate.Mybatis Plus.MyBatis-PageHelper MyBatis-Generate 使用 Mybatis Generator ...

  6. 用 Docker Machine 创建 Azure 虚拟主机

    搭建环境向来是一个重复造轮子的过程,Docker Machine 则把用户搭建 Docker 环境的各种方案汇集在了一起.笔者在<Docker Machine 简介>一文中演示了使用 Do ...

  7. LogMiner的使用

    LogMiner是用于Oracle日志挖掘的利器. 百科解释: LogMiner 是Oracle公司从产品8i以后提供的一个实际非常有用的分析工具,使用该工具可以轻松获得Oracle 重做日志文件(归 ...

  8. JS面向对象一

    面向对象分为三大类 封装,继承,多态! 封装就是在一个函数方法中嵌套另外一个函数方法,外层函数方法返回内层函数方法里面的结果,其中内层函数要调用外层函数定义的局部变量 每个函数方法就是一个局部作用域, ...

  9. 定制Android开发者专属T恤

    之前在T社上买了一件定制的T恤,感觉质量挺不错的,那是段子张发起的众筹.正面有hello google这几个字母. 我自己本身是一个Android粉,从nexus手机到pixel手机,坚持买原生的操作 ...

  10. 简单粗暴的在vmware虚拟机中固定ip

    虚拟机对于很多做测试的或者在学习测试中的人来说是位常客,经常会用到,但是虚拟机重启之后,很多人遇到虚拟机ip变化,很是头痛,我在学习过程中也遇到了这个问题,百度了很多办法,有些办法对于网络知识小白来说 ...