[HDU4787]GRE Words Revenge 解题报告
这是我之前博客里提到的一道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 解题报告的更多相关文章
- HDU-4787 GRE Words Revenge 解题报告
这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题. 这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合 ...
- HDU4787 GRE Words Revenge【AC自动机 分块】
HDU4787 GRE Words Revenge 题意: \(N\)次操作,每次记录一个\(01\)串或者查询一个\(01\)串能匹配多少个记录的串,强制在线 题解: 在线的AC自动机,利用分块来降 ...
- HDU4787 GRE Words Revenge(AC自动机 分块 合并)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4787 Description Now Coach Pang is preparing for ...
- 2011 ACM-ICPC 成都赛区解题报告(转)
2011 ACM-ICPC 成都赛区解题报告 首先对F题出了陈题表示万分抱歉,我们都没注意到在2009哈尔滨赛区曾出过一模一样的题.其他的话,这套题还是非常不错的,除C之外的9道题都有队伍AC,最终冠 ...
- CH Round #56 - 国庆节欢乐赛解题报告
最近CH上的比赛很多,在此会全部写出解题报告,与大家交流一下解题方法与技巧. T1 魔幻森林 描述 Cortana来到了一片魔幻森林,这片森林可以被视作一个N*M的矩阵,矩阵中的每个位置上都长着一棵树 ...
- 二模13day1解题报告
二模13day1解题报告 T1.发射站(station) N个发射站,每个发射站有高度hi,发射信号强度vi,每个发射站的信号只会被左和右第一个比他高的收到.现在求收到信号最强的发射站. 我用了时间复 ...
- BZOJ 1051 最受欢迎的牛 解题报告
题目直接摆在这里! 1051: [HAOI2006]受欢迎的牛 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 4438 Solved: 2353[S ...
- 习题:codevs 2822 爱在心中 解题报告
这次的解题报告是有关tarjan算法的一道思维量比较大的题目(真的是原创文章,希望管理员不要再把文章移出首页). 这道题蒟蒻以前做过,但是今天由于要复习tarjan算法,于是就看到codevs分类强联 ...
- 习题:codevs 1035 火车停留解题报告
本蒟蒻又来写解题报告了.这次的题目是codevs 1035 火车停留. 题目大意就是给m个火车的到达时间.停留时间和车载货物的价值,车站有n个车道,而火车停留一次车站就会从车载货物价值中获得1%的利润 ...
随机推荐
- c#多线程中Lock()关键字的用法小结
本篇文章主要是对c#多线程中Lock()关键字的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助 本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段 ...
- Netty源码分析第5章(ByteBuf)---->第6节: 命中缓存的分配
Netty源码分析第6章: ByteBuf 第六节: 命中缓存的分配 上一小节简单分析了directArena内存分配大概流程, 知道其先命中缓存, 如果命中不到, 则区分配一款连续内存, 这一小节带 ...
- 2019 年软件开发人员必学的编程语言 Top 3
AI 前线导读:这篇文章将探讨编程语言世界的现在和未来,这些语言让新一代软件开发者成为这个数字世界的关键参与者,他们让这个世界变得更健壮.连接更加紧密和更有意义.开发者要想在 2019 年脱颖而出,这 ...
- Google hack语法
基础语法: 1.语法说明: inurl: 在url地址栏中显示的信息页面 intext: 显示在正文信息中的内容页面 site: 限制显示你某个域名的所有页面 filetype: 搜索文件的后缀或者扩 ...
- spring boot的maven项目报404错误
$.ajax({ async: false, type: "POST", url:'searchFileSource', contentType : "applicati ...
- YQCB冲刺周第四天
上图站立会议 任务看板: 今天的任务:做登录身份的验证,区别普通用户和超级管理员 遇到的困难:中文乱码问题
- The last time the sprint(最后一个冲刺)
经过一两个月的努力,我们终于是做出来了一点东西,从一开始接触这个项目开始,从完全不知道怎么去入手到跌跌碰碰,再到现在可以拿出来一点东西给别人看,我觉得很开心,或许我的这个成品在别人眼中并不算是什么,但 ...
- Unity3D游戏开发——编程实现游戏管理器
本篇简介 本篇介绍了如何将上一篇的设计模式思想运用到实际的开发过程中. 脚本文件 (1)IGameManager:这个接口存在声明了一个属性(一个拥有getter函数的变量,属性的类型是Manager ...
- grunt入门讲解2:如何使用 Gruntfile 配置任务
Grunt的task配置都是在 Gruntfile 中的grunt.initConfig方法中指定的.此配置主要包括以任务名称命名的属性,和其他任意数据.一旦这些代表任意数据的属性与任务所需要的属性相 ...
- [转帖]超能课堂 CPU制作过程
http://www.expreview.com/50814.html 一般来说,我们对IC芯片的了解仅限于它概念,但是对于已经应用到各式各样的数码产品中IC芯片是怎么来的?大家可能只知道制作IC芯片 ...