luogu5212/bzoj2555 substring(后缀自动机+动态树)
对字符串构建一个后缀自动机.
每次查询的就是在转移边上得到节点的parent树中后缀节点数量.
由于强制在线,可以用动态树维护后缀自动机parent树的子树和。
注意一个玄学的优化:每次在执行连边操作时,让parent节点作为x,新节点作为y,否则在一串字符相同的串会被莫名其妙卡成N方。
压行后的lct和sam
#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
struct node { int len, link, sz; map<char, int> next; } sam[1200010];
char tmp[3000010], tmp1[233];
int ch[1200010][2], fa[1200010], st[1200010], sz[1200010], s[1200010], mask, qcnt, tot, last;
bool lazy[1200010], val[1200010];
void decode() { for (int j = 0, mask1 = mask, len = strlen(tmp); j < len; j++) swap(tmp[j], tmp[mask1 = (mask1 * 131 + j) % len]); }
bool nroot(int x) { return ch[fa[x]][0] == x || ch[fa[x]][1] == x; }
void rev(int x) { swap(ch[x][0], ch[x][1]), lazy[x] ^= 1; }
void pushup(int x) { sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + s[x] + val[x]; }
void pushdown(int x) { if (lazy[x]) { if (ch[x][0]) { rev(ch[x][0]); } if (ch[x][1]) { rev(ch[x][1]); } lazy[x] = 0; } }
void rotate(int x)
{
int y = fa[x], z = fa[y], k = ch[y][1] == x, w = ch[x][k ^ 1];
if (nroot(y)) { ch[z][ch[z][1] == y] = x; } ch[x][k ^ 1] = y, ch[y][k] = w;
if (w) { fa[w] = y; } fa[y] = x; fa[x] = z; pushup(y), pushup(x);
}
void splay(int x)
{
int y = x, top = 0; st[++top] = y; while (nroot(y)) { st[++top] = y = fa[y]; } while (top > 0) { pushdown(st[top--]); }
while (nroot(x)) { int y = fa[x], z = fa[y]; if (nroot(y)) { rotate((ch[y][1] == x) ^ (ch[z][1] == y) ? x : y); } rotate(x); }
}
void access(int x) { for (int y = 0; x > 0; x = fa[y = x]) splay(x), s[x] += sz[ch[x][1]] - sz[y], ch[x][1] = y, pushup(x); }
void makert(int x) { access(x), splay(x), rev(x); }
int findrt(int x) { access(x), splay(x); while (ch[x][0]) { pushdown(x), x = ch[x][0]; } return x; }
void link(int x, int y) { makert(x); if (findrt(y) != x) fa[x] = y, s[y] += sz[x], pushup(y); }
void cut(int x, int y) { makert(x); if (findrt(y) == x && fa[x] == y && ch[x][1] == 0) ch[y][0] = fa[x] = 0, pushup(y); }
void insert(char ch)
{
int cur = ++tot, p = last; sam[cur].len = sam[last].len + 1, sam[cur].sz = 1, sam[cur].next.clear(), sz[tot] = val[tot] = 1;
while (p != -1 && sam[p].next.count(ch) == false) sam[p].next[ch] = cur, p = sam[p].link;
if (p == -1) { sam[cur].link = 1, link(1, cur); }
else
{
int q = sam[p].next[ch];
if (sam[p].len + 1 == sam[q].len) { sam[cur].link = q, link(q, cur); }
else
{
int cjh = ++tot;
sam[cjh].len = sam[p].len + 1, sam[cjh].link = sam[q].link, link(sam[q].link, cjh), sam[cjh].next = sam[q].next;
while (p != -1 && sam[p].next[ch] == q) sam[p].next[ch] = cjh, p = sam[p].link;
if (sam[q].link) { cut(sam[q].link, q); } sam[q].link = sam[cur].link = cjh, link(cjh, q), link(cjh, cur);
}
}
last = cur;
}
int main()
{
tot = 1, last = 1; sam[1].len = 0, sam[1].link = -1, sam[1].sz = 1, sam[1].next.clear(), sz[tot] = val[tot] = 1;
scanf("%d%s", &qcnt, tmp); for (int j = 0; tmp[j] != 0; j++) insert(tmp[j]);
for (int i = 1; i <= qcnt; i++)
{
scanf("%s%s", tmp1, tmp), decode();
if (!strcmp(tmp1, "ADD")) for (int j = 0; tmp[j] != 0; j++) insert(tmp[j]);
else
{
int p = 1;
for (int j = 0; tmp[j] != 0; j++)
if (sam[p].next.count(tmp[j])) p = sam[p].next[tmp[j]];
else { printf("0\n"); goto escape; }
cut(p, sam[p].link), makert(p);
int ans = sz[p];
link(sam[p].link, p);
printf("%d\n", ans), mask ^= ans;
} escape:;
}
return 0;
}
luogu5212/bzoj2555 substring(后缀自动机+动态树)的更多相关文章
- BZOJ3413: 匹配(后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并... 首先可以转化一下模型(想不到qwq):问题可以转化为统计\(B\)中每个前缀在\(A\)中出现的次数.(画一画就出来了) 然后直 ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- 洛谷P2178 [NOI2015]品酒大会(后缀自动机 线段树)
题意 题目链接 Sol 说一个后缀自动机+线段树的无脑做法 首先建出SAM,然后对parent树进行dp,维护最大次大值,最小次小值 显然一个串能更新答案的区间是\([len_{fa_{x}} + 1 ...
- BZOJ1396: 识别子串(后缀自动机 线段树)
题意 题目链接 Sol 后缀自动机+线段树 还是考虑通过每个前缀的后缀更新答案,首先出现次数只有一次,说明只有\(right\)集合大小为\(1\)的状态能对答案产生影响 设其结束位置为\(t\),代 ...
- [Luogu5161]WD与数列(后缀数组/后缀自动机+线段树合并)
https://blog.csdn.net/WAautomaton/article/details/85057257 解法一:后缀数组 显然将原数组差分后答案就是所有不相交不相邻重复子串个数+n*(n ...
- 洛谷P4493 [HAOI2018]字串覆盖(后缀自动机+线段树+倍增)
题面 传送门 题解 字符串就硬是要和数据结构结合在一起么--\(loj\)上\(rk1\)好像码了\(10k\)的样子-- 我们设\(L=r-l+1\) 首先可以发现对于\(T\)串一定是从左到右,能 ...
- 模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合)
模板—字符串—后缀自动机(后缀自动机+线段树合并求right集合) Code: #include <bits/stdc++.h> using namespace std; #define ...
- 【BZOJ4556】[TJOI2016&HEOI2016] 字符串(后缀自动机+线段树合并+二分)
点此看题面 大致题意: 给你一个字符串\(s\),每次问你一个子串\(s[a..b]\)的所有子串和\(s[c..d]\)的最长公共前缀. 二分 首先我们可以发现一个简单性质,即要求最长公共前缀,则我 ...
- bzoj3879 SvT(后缀自动机+虚树)
bzoj3879 SvT(后缀自动机+虚树) bzoj 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置 ...
随机推荐
- Maven整合SSM框架——详细整合教程(Spring+SpringMVC+MyBatis)
使用SSM(Spring.SpringMVC和Mybatis)已经有三个多月了,项目在技术上已经没有什么难点了,基于现有的技术就可以实现想要的功能,当然肯定有很多可以改进的地方.之前没有记录SSM整合 ...
- oracle查询列合并为行(listagg简单用法)
今天工作时遇见一个数据查询分组问题,就是将分组后同一组数据某一列合并为一行,因为之前很少用到,这次工作中刚好有用到,所以手痒难耐,将它记录下来. 查询sql如下: select t.province_ ...
- 你是真的了解ssh吗 说说你不知道的ssh
Ssh命令——基石天赋 主要参数说明: -l 指定登入用户 -p 设置端口号 -f 后台运行,并推荐加上 -n 参数 -n 将标准输入重定向到 /dev/null,防止读取标准输入 -N 不执行远程命 ...
- web服务器推送技术
传统模式的 Web 系统以客户端发出请求.服务器端响应的方式工作.不能满足很多现实应用的需求,譬如: 监控系统:后台硬件温度.电压发生变化: 即时通信系统:其它用户登录.发送信息: 即时报价系统:后台 ...
- dataTable写入数据库(大数据写入)
例1: connectionStr,链接字符串dataTableName, 数据库中对应表名sourceDataTable DataTable 要写入数据库的DataTable字段要和表一致 publ ...
- 680. Valid Palindrome II 对称字符串-可删字母版本
[抄题]: Given a non-empty string s, you may delete at most one character. Judge whether you can make i ...
- NOIP2018 解题笔记
D1T1 铺设道路 在场上并没有想到积木大赛这道原题. 差分之后可以把在$[l, r]$这段区间$ - 1$变成在$l$处$ - 1$,在$r + 1$处$ + 1$,然后最终目标是使$\forall ...
- 打印单据,A4纸,每个单据占一个A4纸,两个单据之间不挨着
打印单据,A4纸,每个单据占一个A4纸,两个单据之间不挨着 <style type="text/css" media="print">.Noprin ...
- Shiro——概述
Apache Shiro 是 Java 的一个安全(权限)框架. Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在 JavaEE 环境. Shiro 可以完成 ...
- getContextPath、getServletPath、getRequestURI,getRealPath的区别
假定你的web application 项目名称为news,你在浏览器中输入请求路径: http://localhost:8080/news/main/list.jsp 则执行下面向行代码后打印出如下 ...