这是我之前博客里提到的一道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;
}

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

[HDU4787]GRE Words Revenge 解题报告的更多相关文章

  1. HDU-4787 GRE Words Revenge 解题报告

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

  2. HDU4787 GRE Words Revenge【AC自动机 分块】

    HDU4787 GRE Words Revenge 题意: \(N\)次操作,每次记录一个\(01\)串或者查询一个\(01\)串能匹配多少个记录的串,强制在线 题解: 在线的AC自动机,利用分块来降 ...

  3. HDU4787 GRE Words Revenge(AC自动机 分块 合并)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4787 Description Now Coach Pang is preparing for ...

  4. 2011 ACM-ICPC 成都赛区解题报告(转)

    2011 ACM-ICPC 成都赛区解题报告 首先对F题出了陈题表示万分抱歉,我们都没注意到在2009哈尔滨赛区曾出过一模一样的题.其他的话,这套题还是非常不错的,除C之外的9道题都有队伍AC,最终冠 ...

  5. CH Round #56 - 国庆节欢乐赛解题报告

    最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...

  6. 二模13day1解题报告

    二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...

  7. BZOJ 1051 最受欢迎的牛 解题报告

    题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 4438  Solved: 2353[S ...

  8. 习题:codevs 2822 爱在心中 解题报告

    这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...

  9. 习题:codevs 1035 火车停留解题报告

    本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...

随机推荐

  1. SpringMVC 完美解决PUT请求参数绑定问题(普通表单和文件表单)

    一 解决方案 修改web.xml配置文件 将下面配置拷贝进去(在原有的web-app节点里面配置 其它配置不变) <!-- 处理PUT提交参数(只对基础表单生效) --> <filt ...

  2. OpenGL(1)-环境搭建

    写在前面 工作几年,开始沉心做技术,对自己的知识进行梳理. OpenGL是由khronos组织制定并维护的规范,并不是API. OpenGL在3.2之前采用的是立即渲染模式(固定渲染管线),3.2之后 ...

  3. Kubernetes服务发现之Service详解

    一.引子 Kubernetes Pod 是有生命周期的,它们可以被创建,也可以被销毁,然后一旦被销毁生命就永远结束.通过ReplicationController 能够动态地创建和销毁Pod(列如,需 ...

  4. .Net 如何访问主流的各大数据库

    做过开发的都知道,.NET基本可以理解是和MSSQL,windows服务器属于一个好的搭档,正如PHP和MYSQL,LIUNX等也可以理解是一个完美搭配:但是在实际的开发中并不完全是这样的,如果你是学 ...

  5. 客户端传入数据的校验-RestController进阶

    使用Hibernate Validator进行数据校验 Bean Validation注解(需要加入相关依赖,在SpringBoot中可以直接使用,SpringBoot会帮我们直接加入) @Null ...

  6. 北美跨境电商平台Wish透露未来一年在华规划

    9月12日,北美跨境电商平台Wish在深圳透露了未来一年在中国区的重点规划.Wish中国区总裁丁浩川表示,在下一阶段,Wish公司将继续围绕 提升平台流量. 加强品类支撑. 深化库存管理. 推进物流改 ...

  7. (第十周)新NABCD

    项目名:食物链教学工具 组名:奋斗吧兄弟 组长:黄兴 组员:李俞寰.杜桥.栾骄阳.王东涵 新的NABCD模型: Need:可以辅助教师课堂讲授食物链相关的知识.软件的界面要漂亮,操作要简单,要给出软件 ...

  8. 团队项目-NABCD

    用户需求分析与NABCD 模拟经营类(SIM)游戏:玩家模拟经营一家软件公司,平台初步定为Android. Need需求 任何一款游戏都要有自己的定位和目标群体,这些 iiMediaResearch数 ...

  9. JAVA第一次实验 ——凯撒密码

    课程:Java程序设计 班级:1352 姓名:黄伟业 学号:20135215 成绩:            指导教师:娄嘉鹏  实验日期:2015.4.15 实验密级: 预习程度:  实验时间:19: ...

  10. 2018-2019-20172321 《Java软件结构与数据结构》第九周学习总结

    2018-2019-20172321 <Java软件结构与数据结构>第九周学习总结 教材学习内容总结 第15章 图 无向图 图由顶点和边组成. 顶点由名字或标号来表示,如:A.B.C.D: ...