题目传送门

题目大意

给出 \(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的更多相关文章

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

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

  2. GRE Words Revenge AC自动机 二进制分组

    GRE Words Revenge 题意和思路都和上一篇差不多. 有一个区别就是需要移动字符串.关于这个字符串,可以用3次reverse来转换, 前面部分翻转一下, 后面部分翻转一下, 最后整个串翻转 ...

  3. ●HDU 4787 GRE Words Revenge

    题链: http://acm.hdu.edu.cn/showproblem.php?pid=4787 题解: AC自动机(强制在线构造) 题目大意: 有两种操作, 一种为:+S,表示增加模式串S, 另 ...

  4. HDU 4787 GRE Words Revenge

    Description Now Coach Pang is preparing for the Graduate Record Examinations as George did in 2011. ...

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

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

  6. [HDU 4787] GRE Words Revenge (AC自动机)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4787 题目大意: 给你若干个单词,查询一篇文章里出现的单词数.. 就是被我水过去的...暴力重建AC自 ...

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

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

  8. [HDU4787]GRE Words Revenge 解题报告

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

  9. 题解 [CF332C] Students' Revenge

    题面 解析 辣鸡题面毁我青春 因为翻译的题面中写了一句\(剩下的n−k个不会完成\). 所以就以为剩下的\(n-k\)个都会算上不满意值. (然而事实是只有\(p-k\)个...) 首先根据主席的规则 ...

随机推荐

  1. [源码解析] 深度学习流水线并行 GPipe(3) ----重计算

    [源码解析] 深度学习流水线并行 GPipe(3) ----重计算 目录 [源码解析] 深度学习流水线并行 GPipe(3) ----重计算 0x00 摘要 0x01 概述 1.1 前文回顾 1.2 ...

  2. redis BLPOP命令阻塞,非阻塞(读了好久才懂)

    来源于:http://redisdoc.com/list/blpop.html#id1BLPOP key [key -] timeout 可用版本: >= 2.0.0 时间复杂度: O(1) B ...

  3. pgsql基本介绍

    join on 与数学原理 pgsql切换数据库 直接输入 \C youdatabasename 即可 \d 表名 -- 得到表结构 select * from tablename查看表的数据 相信有 ...

  4. HashMap 为什么线程不安全?

    作者:developer http://cnblogs.com/developer_chan/p/10450908.html 我们都知道HashMap是线程不安全的,在多线程环境中不建议使用,但是其线 ...

  5. 灵魂画手:漫画图解 SSH

    OpenSSL 本身是一个软件库,这个软件被广泛的应用在系统服务器当中,他的主要功能是在网络通信的过程中,保证数据的一致性以及数据传输过程中的安全性.软件本身是由C语言编写,这使得他具备了跨平台的特性 ...

  6. Maven专题3——生命周期与插件

    三套生命周期 Maven有3套相互独立的生命周期,用户可以调用某个生命周期的阶段,而不会对其他生命周期产生影响. 每个生命周期包含一些有先后顺序的阶段,后面的阶段依赖于前面的阶段,意味着用户调用后面的 ...

  7. 迷宫2----BFS

    题目 :蒜头君在你的帮助下终于逃出了迷宫,但是蒜头君并没有沉浸于喜悦之中,而是很快的又陷入了思考,从这个迷宫逃出的最少步数是多少呢?输入格式第一行输入两个整数 n 和 m,表示这是一个 n×m 的迷宫 ...

  8. windows 下使用 mingw编译器 调试时 无法跟进源码

    windows 下使用 mingw编译器 调试时 无法跟进源码 最近在公司使用QT 开发,官方在线下载的 安装的QT mingw 都是没有debug版本的 由于没有debug版本动态库 所以你调试的时 ...

  9. PHP获取目录中的全部内容RecursiveDirectoryIterator

    这次我们来介绍一个SPL库中的目录迭代器,它的作用其实非常简单,从名字就可以看出来,就是获取指定目录下的所有内容.之前我们要遍历目录获取目录及目录下的所有文件一般是需要进行递归遍历的,自己写这个代码说 ...

  10. js复制功能代码

    js复制代码: http://www.clipboardjs.cn/