【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了 ...
随机推荐
- 全面解析C#中的异步编程
当我们处理一些长线的调用时,经常会导致界面停止响应或者IIS线程占用过多等问题,这个时候我们需要更多的是用异步编程来修正这些问题,但是通常都是说起来容易做起来难,诚然异步编程相对于同步编程来说,它是一 ...
- 读书笔记:《HTML5开发手册》--figure、time、details、mark
这是补充HTML5基础知识的系列内容,其他为: 一.HTML5-- 新的结构元素 二.HTML5-- figure.time.details.mark 三.HTML5-- details活学活用 四. ...
- [转载]MySQL面试题
1.MySQL的复制原理以及流程基本原理流程,3个线程以及之间的关联:(1)主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中:(2)从:io线程——在使用s ...
- C++ chrono 库中的 steady_clock 和 system_clock
C++11 中提供了一个计时的标准库 <chrono>; 里面有三种时钟 clock: steady_clock, system_clock 和 high_resolution_clock ...
- 使用AD对Linux客户端进行身份验证
https://technet.microsoft.com/zh-cn/library/2008.12.linux.aspx
- Netty源码分析第2章(NioEventLoop)---->第7节: 处理IO事件
Netty源码分析第二章: NioEventLoop 第七节:处理IO事件 上一小节我们了解了执行select()操作的相关逻辑, 这一小节我们继续学习select()之后, 轮询到io事件的相关 ...
- Erlang数据类型的表示和实现(1)——数据类型回顾
本文介绍 Erlang 语言中使用的各种数据类型以及这些数据类型在 Erlang 虚拟机内部的表示和实现.了解数据类型的实现可以帮助大家在实际开发过程中正确选择数据类型,并且可以更好更高效地操作这些数 ...
- 为什么每次进入命令都要重新source /etc/profile 才能生效?
https://segmentfault.com/q/1010000005981201
- PHP版本对比【转】
其他历史http://www.cnblogs.com/yjf512/p/3588466.html php5.3 改动: 1.realpath() 现在是完全与平台无关的. 结果是非法的相对路径比如FI ...
- 2018年第九届蓝桥杯【C++省赛B组】
2标题:明码 汉字的字形存在于字库中,即便在今天,16点阵的字库也仍然使用广泛.16点阵的字库把每个汉字看成是16x16个像素信息.并把这些信息记录在字节中. 一个字节可以存储8位信息,用32个字节就 ...