【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set
题目描述
输入
输出
样例输入
aba-caba
样例输出
1
3
5
3
6
9
12
17
题解
广义后缀自动机+树链的并+STL-set
题目给出的字符串是一棵Trie的形式,我们对其建出广义后缀自动机。
那么每次我们要求的就是:Trie树上当前所有点在后缀自动机pre树到根节点的路径所覆盖的所有点的 $dis[pre[i]]-dis[i]$ 之和,即树链的并的长度。
我们使用STL-set维护动态插入删除节点的树链的并即可。
时间复杂度 $O(26n+n\log n)$ 。
第一次写正常的广义SAM = =
#include <set>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200010
using namespace std;
set<int> s;
set<int>::iterator it;
char str[N];
int tc[N][26] , tf[N] , tt = 1 , pos[N] , c[N][26] , pre[N] , dis[N] , tot = 1 , head[N] , to[N] , next[N] , cnt , fa[N][20] , deep[N] , log[N] , vp[N] , rp[N] , tp;
inline int insert(int x , int p)
{
if(c[p][x])
{
int q = c[p][x];
if(dis[q] == dis[p] + 1) return q;
else
{
int nq = ++tot;
memcpy(c[nq] , c[q] , sizeof(c[q]));
dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[q] = nq;
while(p && c[p][x] == q) c[p][x] = nq , p = pre[p];
return nq;
}
}
else
{
int np = ++tot;
dis[np] = dis[p] + 1;
while(p && !c[p][x]) c[p][x] = np , p = pre[p];
if(!p) pre[np] = 1;
else
{
int q = c[p][x];
if(dis[q] == dis[p] + 1) pre[np] = q;
else
{
int nq = ++tot;
memcpy(c[nq] , c[q] , sizeof(c[q]));
dis[nq] = dis[p] + 1 , pre[nq] = pre[q] , pre[np] = pre[q] = nq;
while(p && c[p][x] == q) c[p][x] = nq , p = pre[p];
}
}
return np;
}
}
void build(int x)
{
int i;
for(i = 0 ; i < 26 ; i ++ )
if(tc[x][i])
pos[tc[x][i]] = insert(i , pos[x]) , build(tc[x][i]);
}
inline void add(int x , int y)
{
to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void dfs(int x)
{
int i;
vp[x] = ++tp , rp[tp] = x;
for(i = 1 ; i <= log[deep[x]] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
for(i = head[x] ; i ; i = next[i]) fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , dfs(to[i]);
}
inline int lca(int x , int y)
{
int i;
if(deep[x] < deep[y]) swap(x , y);
for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
if(deep[x] - deep[y] >= (1 << i))
x = fa[x][i];
if(x == y) return x;
for(i = log[deep[x]] ; ~i ; i -- )
if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
x = fa[x][i] , y = fa[y][i];
return fa[x][0];
}
int main()
{
int q , i , now = 1 , x , y;
long long ans = 0;
scanf("%s" , str) , q = strlen(str);
for(i = 0 ; i < q ; i ++ )
{
if(str[i] == '-') now = tf[now];
else
{
if(!tc[now][str[i] - 'a']) tc[now][str[i] - 'a'] = ++tt , tf[tt] = now;
now = tc[now][str[i] - 'a'];
}
}
pos[1] = 1 , build(1);
for(i = 2 ; i <= tot ; i ++ ) add(pre[i] , i) , log[i] = log[i >> 1] + 1;
dfs(1);
now = 1;
for(i = 0 ; i < q ; i ++ )
{
if(str[i] == '-')
{
ans -= dis[pos[now]] , s.erase(vp[pos[now]]);
x = y = 0 , it = s.upper_bound(vp[pos[now]]);
if(it != s.end()) x = rp[*it];
if(it != s.begin()) y = rp[*--it];
if(x) ans += dis[lca(pos[now] , x)];
if(y) ans += dis[lca(pos[now] , y)];
if(x && y) ans -= dis[lca(x , y)];
now = tf[now];
}
else
{
now = tc[now][str[i] - 'a'] , ans += dis[pos[now]];
x = y = 0 , it = s.upper_bound(vp[pos[now]]);
if(it != s.end()) x = rp[*it];
if(it != s.begin()) y = rp[*--it];
if(x) ans -= dis[lca(pos[now] , x)];
if(y) ans -= dis[lca(pos[now] , y)];
if(x && y) ans += dis[lca(x , y)];
s.insert(vp[pos[now]]);
}
printf("%lld\n" , ans);
}
return 0;
}
【bzoj5084】hashit 广义后缀自动机+树链的并+STL-set的更多相关文章
- CF G. Indie Album 广义后缀自动机+树链剖分+线段树合并
这里给出一个后缀自动机的做法. 假设每次询问 $t$ 在所有 $s$ 中的出现次数,那么这是非常简单的: 直接对 $s$ 构建后缀自动机,随便维护一下 $endpos$ 大小就可以. 然而,想求 $t ...
- CF504E Misha and LCP on Tree 后缀自动机+树链剖分+倍增
求树上两条路径的 LCP (树上每个节点代表一个字符) 总共写+调了6个多小时,终于过了~ 绝对是我写过的最复杂的数据结构了 我们对这棵树进行轻重链剖分,然后把所有的重链分正串,反串插入到广义后缀自动 ...
- [LuoguU41039]PION后缀自动机 树链剖分+动态开点线段树
链接 刚开始看出题人题解都吓蒙掉了,还以为是什么难题,结果就一板子题 思路:对每一个文件名开一棵线段树,然后树剖即可 #include<bits/stdc++.h> #define REP ...
- 【CF666E】Forensic Examination 广义后缀自动机+倍增+线段树合并
[CF666E]Forensic Examination 题意:给你一个字符串s和一个字符串集合$\{t_i\}$.有q个询问,每次给出$l,r,p_l,p_r$,问$s[p_l,p_r]$在$t_l ...
- cf666E. Forensic Examination(广义后缀自动机 线段树合并)
题意 题目链接 Sol 神仙题Orz 后缀自动机 + 线段树合并 首先对所有的\(t_i\)建个广义后缀自动机,这样可以得到所有子串信息. 考虑把询问离线,然后把\(S\)拿到自动机上跑,同时维护一下 ...
- CF 666E Forensic Examination——广义后缀自动机+线段树合并
题目:http://codeforces.com/contest/666/problem/E 对模式串建广义后缀自动机,询问的时候把询问子串对应到广义后缀自动机的节点上,就处理了“区间”询问. 还要处 ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- CF666E Forensic Examination 广义后缀自动机_线段树合并_树上倍增
题意: 给定一个串 $S$ 和若干个串 $T_{i}$每次询问 $S[pl..pr]$ 在 $Tl..Tr$ 中出现的最多次数,以及出现次数最多的那个串的编号. 数据范围: 需要离线 题解:首先,很常 ...
- UVA - 11107 Life Forms (广义后缀自动机+后缀树/后缀数组+尺取)
题意:给你n个字符串,求出在超过一半的字符串中出现的所有子串中最长的子串,按字典序输出. 这道题算是我的一个黑历史了吧,以前我的做法是对这n个字符串建广义后缀自动机,然后在自动机上dfs,交上去AC了 ...
随机推荐
- Python爬虫入门 之 如何在豆瓣中获取自己喜欢的TOP N电影信息
什么是爬虫 按照一定规则自动的获取互联网上的信息(如何快速有效的利用互联网上的大量信息) 爬虫的应用 搜索引擎(Google.百度.Bing等搜索引擎,辅助人们检索信息) 股票软件(爬取股票数据,帮助 ...
- 关于AutoMapper和WCF的一些认识
现在互联网时代呈尚快速开发,快速迭代.伴随着必然产生一些好用的第三方工具,今天有幸看到了Automapper这种类似ORM的框架,但是跟ORM还不太一样, ORM是实体和数据库表之间的映射,而此框架主 ...
- Security Permissions Caching
Security Permissions Caching Security permission caching is implemented in Security Adapters - class ...
- 【总结】浅谈ref与out
ref——仅仅是一个地址 (1)当一个方法或函数在使用ref作为参数时,在方法中或函数中对ref参数所做的更改都将反映在该变量中. (2)如果要使用ref参数,则必须将参数作为ref显示传递到方法中. ...
- Scrapy中的POST请求发送和递归爬取
POST请求发送 重写爬虫应用文件中继承Spider类的 类的里面的start_requests(self)这个方法 def start_requests(self): #请求的url post_ur ...
- 利用workbench对linux/Ubuntu系统中的mysql数据库进行操作
在上一篇文章中,我分享了在linux中如何安装mysql数据库,但是这只是安装了mysql的服务,并没有图形化管理界面,所以这样子操作起来并没有那么方便,那么现在我们就来实现如何利用在window中安 ...
- JavaScript判断对象是否是NULL(转)
写js经常会遇到非空判断,看了你不就像风一样的文章 自己没有做总结,特地转载.很有帮助 function isEmpty(obj) { // 检验 undefined 和 null if (!obj ...
- java事务 深入Java事务的原理与应用
一.什么是JAVA事务 通常的观念认为,事务仅与数据库相关. 事务必须服从ISO/IEC所制定的ACID原则.ACID是原子性(atomicity).一致性(consistency).隔离性 (iso ...
- 0.前言 three.js 简介
前言 前段时间开始接触three.js本来以为会很简单但是真正开始学习的时候才发现事情并不是我想象的那么容易,three.js的学习资料非常的少稍微好一点的资料就是收费的,给three.js的学习带来 ...
- 报错android.view.InflateException: Binary XML file line #11: Attempt to invoke virtual method 'boolean
出现这种问题,打开Android monitor的调试信息发现是 android.view.InflateException: Binary XML file line #11: Attempt to ...