题解 GRE Words Revenge
题目大意
给出 \(m\) 次操作,分别为以下两种操作:
学习一个单词
给出一个段落,查询里面有多少个学过的单词。注意,如果学习过 \(\text{ab,bc}\) ,当前查询段落为 \(\text{abc}\) ,那么应该算 \(2\) 个单词。
\(m\le 10^5\),保证学习的单词长度之和 \(\le 10^5\),给出的段落长度之和 \(\le 5\times 10^6\) 。
思路
据说可以直接拿暴力艹过去。。。(不会吧?阿sir?
可以看出来,如果只有一次查询,那其实就是一个裸的AC自动机,于是,问题就变成了如何维护一个动态的AC自动机。但是显然我们不可以,于是问题就是如何搞出一个伪在线AC自动机。
我们发现我们可以开两个AC自动机S1,S2,当S2里面的点数 \(\ge \sqrt n\) 的时候我们就直接把S1,S2进行暴力合并,否则暴力重构S2,就有点像根号分治。
然后我们通过分析发现我们的时间复杂度其实是 \(\Theta(n\sqrt n+m)\) 的,其中 \(n\) 是AC自动机里面的点数。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define MAXN 5000005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
struct ACAM{
bool flag;//表示是否需要建立失配指针。
int tot,ch[MAXN][2],cnt[MAXN],fail[MAXN],last[MAXN];//fail表示失配指针,last表示上次有贡献的点,0是根,cnt表示是否是结束节点
void clear (){
for (Int i = 0;i <= tot;++ i) ch[i][0] = ch[i][1] = cnt[i] = 0;
tot = 0,flag =0;
}
void buildfail (){
queue <int> q;
for (Int i = 0;i < 2;++ i){
if (!ch[0][i]) continue;
q.push (ch[0][i]),fail[ch[0][i]] = last[ch[0][i]] = 0;
}
while (!q.empty()){
int u = q.front();q.pop ();
for (Int i = 0;i < 2;++ i){
int v = ch[u][i];
if (!v) continue;
int p = fail[u];while (p && !ch[p][i]) p = fail[p];
fail[v] = ch[p][i],last[v] = cnt[fail[v]] ? fail[v] : last[fail[v]];
q.push (v);
}
}
}
void insert (char *s){
flag = 1;int x = 0;
for (Int i = 0;s[i];++ i){
int now = s[i] - '0';
if (!ch[x][now]) ch[x][now] = ++ tot;
x = ch[x][now];
}
cnt[x] = 1;
}
int calc (int u){
int res = 0;
while (u){
res += cnt[u];
u = last[u];
}
return res;
}
int match (char *s){
if (flag) buildfail (),flag = 0;
int res = 0,x = 0;
for (Int i = 0;s[i];++ i){
int now = s[i] - '0';
while (x && !ch[x][now]) x = fail[x];
x = ch[x][now];
res += calc (x);
}
return res;
}
void Merge (ACAM &ot,int u,int v){
flag = 1;
for (Int i = 0;i < 2;++ i){
if (!ot.ch[v][i]) continue;
if (!ch[u][i]) ch[u][i] = ++ tot;
Merge (ot,ch[u][i],ot.ch[v][i]);
}
cnt[u] |= ot.cnt[v];
}
}SM[2];
char s[MAXN],t[MAXN];
void Solve (){
int n;read (n);
SM[0].clear(),SM[1].clear();
int lastans = 0,up = sqrt (MAXN);
while (n --> 0){
scanf ("%s",s);
int len = strlen (s + 1),shift = lastans % len;
t[len] = '\0';for (Int i = 0;i < len;++ i) t[i] = s[(i + shift) % len + 1];
if (s[0] == '?') write (lastans = SM[0].match (t) + SM[1].match (t)),putchar ('\n');
else{
SM[1].insert (t);
if (SM[1].tot > up) SM[0].Merge (SM[1],0,0),SM[1].clear();
}
}
}
signed main(){
int t;read (t);
for (Int i = 1;i <= t;++ i) printf ("Case #%d:\n",i),Solve ();
return 0;
}
题解 GRE Words Revenge的更多相关文章
- HDU4787 GRE Words Revenge【AC自动机 分块】
HDU4787 GRE Words Revenge 题意: \(N\)次操作,每次记录一个\(01\)串或者查询一个\(01\)串能匹配多少个记录的串,强制在线 题解: 在线的AC自动机,利用分块来降 ...
- GRE Words Revenge AC自动机 二进制分组
GRE Words Revenge 题意和思路都和上一篇差不多. 有一个区别就是需要移动字符串.关于这个字符串,可以用3次reverse来转换, 前面部分翻转一下, 后面部分翻转一下, 最后整个串翻转 ...
- ●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自动机 分块 合并)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=4787 Description Now Coach Pang is preparing for ...
- [HDU 4787] GRE Words Revenge (AC自动机)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4787 题目大意: 给你若干个单词,查询一篇文章里出现的单词数.. 就是被我水过去的...暴力重建AC自 ...
- HDU-4787 GRE Words Revenge 解题报告
这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题. 这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合 ...
- [HDU4787]GRE Words Revenge 解题报告
这是我之前博客里提到的一道AC自动机的练手题,但是要完成这道题,我之前博客里提到的东西还不够,这里总结一下这道题. 这道题不是一般的裸的AC自动机,它的询问和插入是交叉出现的所以用我之前写的板子不大合 ...
- 题解 [CF332C] Students' Revenge
题面 解析 辣鸡题面毁我青春 因为翻译的题面中写了一句\(剩下的n−k个不会完成\). 所以就以为剩下的\(n-k\)个都会算上不满意值. (然而事实是只有\(p-k\)个...) 首先根据主席的规则 ...
随机推荐
- CSS样式下border的几种线型
在用border的时候经常会忘记它有多少种线型以及各种线型的写法:每次都得从头开始,或是用到Google.百度之类的,有空整理了一下 (1)none (没有边框,无论边框宽度设为多大) (2)dott ...
- Java反射的浅显理解
一.回顾反射相关的知识 1.在xml文件中使用反射的好处: 1)代码更加灵活,后期维护只需要修改配置文件即可 · 初学者一般习惯于在代码本身上直接修改,后期也可以修改配置文件达到相同的目的 · 修改配 ...
- SpringBoot整合定时任务----Scheduled注解实现(一个注解全解决)
一.使用场景 定时任务在开发中还是比较常见的,比如:定时发送邮件,定时发送信息,定时更新资源,定时更新数据等等... 二.准备工作 在Spring Boot程序中不需要引入其他Maven依赖 (因为s ...
- 如何在RHEL7或CentOS 7系统下修改网卡名称(亲测有效~!)
亲测有效的更改RHEL7或CentOS 7的网卡名称的方法, 按照以下4步来操作就可以实现! Step 1 :网卡配置文件名称重命名为eth0[root@localhost ~]# ifconfige ...
- 二、grep文本搜索工具
grep命令作为Unix中用于文本搜索的神奇工具,能够接受正则表达式,生成各种格式的输出.除此外,它还有大量有趣的选项. # 搜索包含特定模式的文本行: [root@centos8 ~]#grep p ...
- springboot通过AOP和自定义注解实现权限校验
自定义注解 PermissionCheck: package com.mgdd.sys.annotation; import java.lang.annotation.*; /** * @author ...
- noip模拟测试18
打开比赛第一眼--超级树? 点开--原题 百感交集-- 欣喜于发现是半年前做过两遍的原题 紧张于如果A不了比较尴尬 绝望于发现根本不会做了 瞟了一眼t1,瞅了一眼t2,嗯--开始搞t3 10分钟打完暴 ...
- Java - 注释、标识符、关键字
背景 要开始磕 Java 了,虽然以前学过用过,但是差不多忘光光了... 现在直接搬狂神的视频素材,不再自己总结,要学的东西太多了... 注释 单行注释 // 多行注释 /* */ 文档注释 /** ...
- Linux - 安装 ant
官方下载地址 https://ant.apache.org/bindownload.cgi 旧版下载地址 https://archive.apache.org/dist/ant/binaries/ 挑 ...
- java多线程 synchronized 与lock锁 实现线程安全
如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. 通过卖火车票的例子 火车站要卖票,我们模 ...