[LOJ#6198]谢特[后缀数组+trie+并查集]
题意
给你一个长度为 \(n\) 的字符串,问 \(LCP(i,j)+(w_i\ xor\ w_j)\) 的最大值,其中 \(LCP\) 表示两个后缀的最长公共前缀。
\(n\le 10^5\)
分析
建立 \(SA\) 之后把所有的 \(height\) 从大到小加入,维护连通块(类似 \(MST\) ),这样可以找到某个 \(height\) 作为两个后缀的 \(LCP\) 长度时的合法的区间,启发式合并 \(trie\) 即可。
或者也可以建 \(SAM\) ,\(parent\) 树上启发式合并 \(trie\) 。
总时间复杂度为 \(O(nlogn)\)。
代码采用后缀数组的方式。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define go(u) for(int i = head[u], v = e[i].to; i; i=e[i].lst, v=e[i].to)
#define rep(i, a, b) for(int i = a; i <= b; ++i)
#define pb push_back
#define re(x) memset(x, 0, sizeof x)
inline int gi() {
int x = 0,f = 1;
char ch = getchar();
while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) { x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
return x * f;
}
template <typename T> inline void Max(T &a, T b){if(a < b) a = b;}
template <typename T> inline void Min(T &a, T b){if(a > b) a = b;}
const int N = 1e5 + 7;
int n, ans;
char s[N];
namespace SA {
int sa[N], x[N], y[N], c[N], h[N];
void pre(int m) {
rep(i, 1, m) c[i] = 0;
rep(i, 1, n) c[x[i] = s[i]] ++;
rep(i, 1, m) c[i] += c[i - 1];
for(int i = n; i; --i) sa[c[x[i]]--] = i;
for(int k = 1; k <= n; k <<= 1) {
int p = 0;
for(int i = n; i >= n - k + 1; --i) y[++p] = i;
rep(i, 1, n) if(sa[i] > k) y[++p] = sa[i] - k;
rep(i, 1, m) c[i] = 0;
rep(i, 1, n) c[x[y[i]]]++;
rep(i, 1, m) c[i] += c[i - 1];
for(int i = n; i; --i) sa[c[x[y[i]]]--] = y[i];
swap(x, y);p = 1; x[sa[1]] = 1;
rep(i, 2, n)
x[sa[i]] = y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p : ++p;
if(p >= n) break;m = p;
}
rep(i, 1, n) x[sa[i]] = i;
for(int i = 1, j = 0; i <= n; ++i) {
if(j) --j;if(sa[i] == 1) continue;
while(s[i + j] == s[sa[x[i] - 1] + j]) ++j;
h[x[i]] = j;
}
}
}
struct data {
int p, h;
bool operator <(const data &rhs) const {
return h > rhs.h;
}
}t[N];
int ndc;
int par[N], rt[N], w[N], ch[N * 20][2];
void ins(int v, int &rt) {
if(!rt) rt = ++ndc;
int u = rt;
for(int i = 16; ~i; --i) {
int c = v >> i & 1;
if(!ch[u][c]) ch[u][c] = ++ndc;
u = ch[u][c];
}
}
int merge(int a, int b) {
if(!a || !b) return a + b;
rep(i, 0, 1) if(ch[a][i] || ch[b][i]) ch[a][i] = merge(ch[a][i], ch[b][i]);
return a;
}
int getans(int dep, int a, int b) {
int res = 0;
rep(i, 0, 1) if(ch[a][i] && ch[b][i ^ 1]) Max(res, getans(dep - 1, ch[a][i], ch[b][i ^ 1]) + (1 << dep));
if(res) return res;
rep(i, 0, 1) if(ch[a][i] && ch[b][i]) Max(res, getans(dep - 1, ch[a][i], ch[b][i]));
return res;
}
int getpar(int a) {
return par[a] == a ? a: par[a] = getpar(par[a]);
}
void Union(int a, int b) {
a = getpar(a), b = getpar(b);
if(a == b) return;
par[b] = a;
rt[a] = merge(rt[a], rt[b]);
}
int main() {
using namespace SA;
n = gi();
scanf("%s", s + 1);
rep(i, 1, n) w[i] = gi();
pre(128);
rep(i, 2, n) t[i - 1] = (data){ i, h[i]};
sort(t + 1, t + n);
rep(i, 1, n) ins(w[sa[i]], rt[i]), par[i] = i;
rep(i, 1, n - 1) {
int f1 = getpar(t[i].p - 1), f2 = getpar(t[i].p), tmp = getans(16, rt[f1], rt[f2]);
Max(ans, tmp + t[i].h);
Union(f1, f2);
}
printf("%d\n", ans);
return 0;
}
[LOJ#6198]谢特[后缀数组+trie+并查集]的更多相关文章
- POJ1743 Musical Theme [后缀数组+分组/并查集]
Musical Theme Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 27539 Accepted: 9290 De ...
- BZOJ 4516: [Sdoi2016]生成魔咒——后缀数组、并查集
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=4516 题意 一开始串为空,每次往串后面加一个字符,求本质不同的子串的个数,可以离线.即长度为 ...
- 【BZOJ4199&UOJ131】品酒大会(后缀数组,并查集)
题意: 两杯“r相似” (r>1)的酒同时也是“1 相似”.“2 相似”.…….“(r−1) 相似”的. n<=300000 abs(a[i])<=10^9 思路:对于i,j两个后缀 ...
- 洛谷P4145 上帝造题的七分钟2/花神游历各国 [树状数组,并查集]
题目传送门 题目背景 XLk觉得<上帝造题的七分钟>不太过瘾,于是有了第二部. 题目描述 "第一分钟,X说,要有数列,于是便给定了一个正整数数列. 第二分钟,L说,要能修改,于是 ...
- loj6198谢特 后缀数组+并查集+Trie
先把问题放在后缀数组上考虑 已知两个数组a b,求min(a[i],...,a[j])+(b[i]^b[j])的最大值 套路题 初始每个点都是一个小连通块 把a按从大到小的顺序加入,计算当前加入边作为 ...
- 谢特——后缀数组+tire 树
题目 [题目描述] 由于你成功地在 $ \text{1 s} $ 内算出了上一题的答案,英雄们很高兴并邀请你加入了他们的游戏.然而进入游戏之后你才发现,英雄们打的游戏和你想象的并不一样…… 英雄们打的 ...
- [学习笔记]可持久化数据结构——数组、并查集、平衡树、Trie树
可持久化:支持查询历史版本和在历史版本上修改 可持久化数组 主席树做即可. [模板]可持久化数组(可持久化线段树/平衡树) 可持久化并查集 可持久化并查集 主席树做即可. 要按秩合并.(路径压缩每次建 ...
- Colored Sticks - poj2513(trie + 并查集)
问题便转化为:给定一个图,是否存在“一笔画”经过涂中每一点,以及经过每一边一次.这样就是求图中是否存在欧拉路Euler-Path.由图论知识可以知道,无向图存在欧拉路的充要条件为:① 图是连通的:② ...
- 字符串 --- KMP Eentend-Kmp 自动机 trie图 trie树 后缀树 后缀数组
涉及到字符串的问题,无外乎这样一些算法和数据结构:自动机 KMP算法 Extend-KMP 后缀树 后缀数组 trie树 trie图及其应用.当然这些都是比较高级的数据结构和算法,而这里面最常用和最熟 ...
随机推荐
- Error Fix – Replication subscriber does not exist on the server anymore(删除Replication时报错的解决办法)
Recently one of my client has faced weird situation related to SQL Server Replication. Their main da ...
- __MySQL 5.7 Replication 相关新功能说明
背景: MySQL5.7在主从复制上面相对之前版本多了一些新特性,包括多源复制.基于组提交的并行复制.在线修改Replication Filter.GTID增强.半同步复制增强等.因为都是和复制相 ...
- 大话存储 1 - 走进计算机IO世界
组成计算机的三大件:CPU,内存和IO. 1 总线 总线就是一条或者多条物理上的导线,每个部件都接到这些导线上,同一时刻只能有一个部件在接收或者发送. 仲裁总线:所有部件按照另一条总线,也就是仲裁总线 ...
- python基础之单例设计模式
class Player(): instance = None init_flag = False def __init__(self): if self.init_flag is False: pr ...
- ICP点云配准原理及优化
ICP算法简介 根据点云数据所包含的空间信息,可以直接利用点云数据进行配准.主流算法为最近迭代算法(ICP,Iterative Closest Point),该算法是根据点云数据首先构造局部几何特征, ...
- Python代码小片段
1.前面变量值的改变不影响后面变量的调用 index=1 index,a=2,index+1 print(a,index) #2 2 2.类的继承(子类实例如何调用父类同名方法) class a: d ...
- 6.Solr4.10.3API使用(CURD)
转载请出自出处:http://www.cnblogs.com/hd3013779515/ 1.在工程中引入solr-solrj-4.10.3.jar <dependency> <gr ...
- Linux基础第一课——基础知识了解
前言 发展历史 linus 林纳斯 赫尔辛基大学 在自己的笔记本上安上自己写的操作系统 基于Linux内核 Linux内核 也是基于unix内核开发出来 unix 不开源 只能军方和大学使用 Linu ...
- python第二十九课——文件读写(复制文件)
自定义函数:实现文件复制操作有形参(2个) 没有返回值相似版(不用) def copyFile(src,dest): #1.打开两个文件:1个关联读操作,1个关联写操作 fr=open(src,'rb ...
- C/C++常用库及工具
值得学习的C语言开源项目 - 1. Webbench Webbench是一个在Linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的 ...