HDU-4787 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;
- }
真是服了这鬼畜的缩进。。。真难看。。。
HDU-4787 GRE Words Revenge 解题报告的更多相关文章
- [HDU 4787] GRE Words Revenge (AC自动机)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4787 题目大意: 给你若干个单词,查询一篇文章里出现的单词数.. 就是被我水过去的...暴力重建AC自 ...
- ●HDU 4787 GRE Words Revenge
题链: http://acm.hdu.edu.cn/showproblem.php?pid=4787 题解: AC自动机(强制在线构造) 题目大意: 有两种操作, 一种为:+S,表示增加模式串S, 另 ...
- HDU 4787 GRE Words Revenge
Description Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. ...
- [HDU4787]GRE Words Revenge 解题报告
这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题. 这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合 ...
- hdu 1556.Color the ball 解题报告
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1556 题目意思:有 n 个气球从左到右排成一排,编号依次为1,2,3,...,n.给出 n 对 a, ...
- hdu 1160 FatMouse's Speed 解题报告
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1160 题目意思:给出一堆老鼠,假设有 n 只(输入n条信息后Ctrl+Z).每只老鼠有对应的weigh ...
- hdu 1879 继续畅通工程 解题报告
题目链接:http://code.hdu.edu.cn/showproblem.php?pid=1879 这条题目我的做法与解决Constructing Roads的解法是相同的. 0 表示没有连通: ...
- hdu 1233 还是畅通工程 解题报告
题目链接:http://code.hdu.edu.cn/showproblem.php?pid=1233 并查集的运用, 实质就是求最小生成树.先对所有的村庄距离从小到大排序,然后判断村庄之间是否属于 ...
- hdu 1213 How Many Tables 解题报告
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1213 有关系(直接或间接均可)的人就坐在一张桌子,我们要统计的是最少需要的桌子数. 并查集的入门题,什 ...
随机推荐
- [0] Devexpress 控件参数集合
gridview控件/统计功能 比如对“数量”列进行统计,只要在GridControl的设计器中设置SummaryItem: SummaryItem.DisplayFormat = "{ ...
- 微信支付生成带logo的二维码
利用到一个qrcode类 比较简洁 原作者没有加入二维码嵌入logo的功能 在这里我进行了小小的修改 可以实现生成微信支付二维码时打上logo 生成png格式的利用到该类中的png方法(我已经改好了) ...
- js实现点击copy,可兼容
在实现功能时写的一个小demo,可以实现点击复制内容(任何你需要copy的文字内容data). 经测试,可兼容 chrome, edge, firefox, ie, opera, safari,至于版 ...
- 愚公oracle数据库同步工具
最近,利用一些时间对oracle数据库实时同步工具做了一些调研分析,主要关注了linkedin的databus和阿里的yugong两个中间件,其中databus需要在每个待同步的表上增加额外的列和触发 ...
- SOD开源框架MSF(消息服务框架)进阶篇
复习:在上一篇我介绍了MSF的基本订阅,模式就是,客户端A,订阅服务器.客户端B,订阅服务器.通过服务器广播消息, 所有订阅过的客户端都能接到消息. 进阶:在上一篇的基础上,增加客户端A,发送信息到服 ...
- Java注解--实现简单读取excel
实现工具类 利用注解实现简单的excel数据读取,利用注解对类的属性和excel中的表头映射,使用Apache的poi就不用在业务代码中涉及row,rows这些属性了. 定义注解: @Retentio ...
- 利用 Traceview 精准定位启动时间测试的异常方法 (工具开源)
机智的防爬虫标识原创博客地址:http://www.cnblogs.com/alexkn/p/7095855.html博客求关注: http://www.cnblogs.com/alexkn 1.启动 ...
- mysql 左连接 右连接 内链接
一般所说的左连接,右连接是指左外连接,右外连接.做个简单的测试你看吧.先说左外连接和右外连接:[TEST1@orcl#16-12月-11] SQL>select * from t1;ID NAM ...
- Kafka 源代码分析之Log
这里分析Log对象本身的源代码. Log类是一个topic分区的基础类.一个topic分区的所有基本管理动作.都在这个对象里完成.类源代码文件为Log.scala.在源代码log目录下. Log类是L ...
- LaTeX初识 新手入门 Texlive和Texmaker学习
转载自:http://blog.sina.com.cn/s/blog_90444ed201016iq6.html http://blog.csdn.net/zb1165048017/article/d ...