[XSY 1551] 往事 广义后缀数组

题意

  给定一棵 n 个点的树, 每条边上有字符 c .

  定义 s[i] 为从 i 到根的所有字符组成的字符串.

  求 $\max_{1 \le u < v \le n} [ LCP(s[u], s[v]) + LCS(s[u], s[v]) ]$ .

  n <= 200000 .

实现1  n log^2 n 广义后缀数组 + 启发式合并

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cstdlib>
  4 #include <cctype>
  5 #include <cmath>
  6 #include <algorithm>
  7 #include <vector>
  8 #include <set>
  9 using namespace std;
 10 #define F(i, a, b) for (register int i = (a); i <= (b); i++)
 11 #define P(i, a, b) for (register int i = (a); i >= (b); i--)
 12 #define LL unsigned long long
 13
 14 namespace Input {
 15     const int S = 2000000;
 16     char s[S], *h = s+S, *t = h;
 17     inline char getchr(void) { if (h == t) fread(s, 1, S, stdin), h = s; return *h++; }
 18     inline int rd(void) {
 19         int f = 1; char c = getchr(); for (; !isdigit(c); c = getchr()) if (c == ‘-‘) f = -1;
 20         int x = 0; for (; isdigit(c); c = getchr()) x = x*10+c-‘0‘; return x*f;
 21     }
 22 }
 23 using Input::rd;
 24
 25 const int N = 200005;
 26 // remember to change back
 27 const int U = 20;
 28 const int M = 307;
 29
 30 int n;
 31 vector<int> g[N];
 32
 33 int u, dep[N];
 34 int par[U][N];
 35 LL p[N], Hash[U][N];
 36
 37 void Build(void) {
 38     n = rd();
 39     u = (int)log2(n);
 40     p[0] = 1;
 41     F(i, 1, n) p[i] = p[i-1] * M;
 42
 43     F(i, 2, n) {
 44         int f = rd(), c = rd() + 1;    // guaranteed that (empty = 0) < (0 + 1)
 45         g[f].push_back(i);
 46         dep[i] = dep[f] + 1;
 47         par[0][i] = f;
 48         Hash[0][i] = c;
 49     }
 50
 51     F(i, 1, u) F(j, 1, n) {
 52         par[i][j] = par[i-1][par[i-1][j]];
 53         Hash[i][j] = Hash[i-1][j] * p[1 << (i-1)] + Hash[i-1][par[i-1][j]];
 54     }
 55 }
 56
 57 int sa[N], rk[N], h[N];
 58 int Log2[N], Min[U][N];
 59
 60 inline int cmp(int x, int y) {
 61     P(i, u, 0)
 62         if (dep[x] >= (1<<i) && dep[y] >= (1<<i) && Hash[i][x] == Hash[i][y])
 63             x = par[i][x], y = par[i][y];
 64     return Hash[0][x] < Hash[0][y];
 65 }
 66 inline int Get(int x, int y) {
 67     int Len = 0;
 68     P(i, u, 0)
 69         if (dep[x] >= (1<<i) && dep[y] >= (1<<i) && Hash[i][x] == Hash[i][y])
 70             x = par[i][x], y = par[i][y], Len += (1<<i);
 71     return Len;
 72 }
 73 inline int LCP(int x, int y) {
 74     // It‘s guaranteed that [x != y]
 75     if (x > y) swap(x, y);
 76     x++;
 77     int c = Log2[y - x + 1];
 78     return min(Min[c][y], Min[c][x + (1 << c) - 1]);
 79 }
 80 void Prework(void) {
 81     F(i, 1, n) sa[i] = i;
 82     sort(sa+1, sa+n+1, cmp);
 83     F(i, 1, n) rk[sa[i]] = i;
 84
 85     F(i, 2, n)
 86         h[i] = Get(sa[i-1], sa[i]);
 87
 88     F(i, 1, n)
 89         Log2[i] = (int)log2(i);
 90
 91     F(i, 1, n) Min[0][i] = h[i];
 92     F(i, 1, u) F(j, 1, n)
 93         Min[i][j] = min(Min[i-1][j], Min[i-1][max(1, j - (1 << (i-1)))]);
 94 }
 95
 96 set<int> s[N];
 97 int Max;
 98
 99 inline void Merge(int x, int y) {
100     if (s[x].size() < s[y].size()) swap(s[x], s[y]);
101     for (set<int>::iterator it = s[y].begin(); it != s[y].end(); it++) {
102         // [ Insert ] and [ Find LCP with *it ]
103         s[x].insert(*it);
104         set<int>::iterator it2 = s[x].find(*it);
105         if (it2 != s[x].begin()) {
106             it2--;
107             Max = max(Max, dep[x] + LCP(*it2, *it));
108             it2++;
109         }
110         if (++it2 != s[x].end())
111             Max = max(Max, dep[x] + LCP(*it2, *it));
112     }
113     s[y].clear();    // release the non-use recollection
114 }
115 void Calc(int x) {
116     s[x].insert(rk[x]);
117     for (vector<int>::iterator it = g[x].begin(); it != g[x].end(); it++) {
118         Calc(*it);
119         Merge(x, *it);
120     }
121 }
122
123 int main(void) {
124     #ifndef ONLINE_JUDGE
125         freopen("recollection.in", "r", stdin);
126     #endif
127
128     Build();
129     Prework();
130     Calc(1); printf("%d\n", Max);
131
132     return 0;
133 }
时间: 2024-08-06 14:57:28

[XSY 1551] 往事 广义后缀数组的相关文章

【XSY1551】往事 广义后缀数组 线段树合并

题目大意 给你一颗trie树,令\(s_i\)为点\(i\)到根的路径上的字符组成的字符串.求\(max_{u\neq v}(LCP(s_u,s_v)+LCS(s_u,s_v))\) \(LCP=\)最长公共前缀,\(LCS=\)最长公共后缀 \(1\leq n\leq 200000\),字符集为\(\{0\ldots 300\}\) 题解 我们先看看这个\(LCP(s_u,s_v)\)怎么求 广义后缀自动机不行.广义后缀树可能可以,但我不会.广义后缀数组可以.然后我就开始手推广义后缀数组 广义

[XSY 1516] 兔子的字符串 后缀数组

题意 给定一个字符串 $S$ . 按照某种方式, 将字符串 $S$ 化成不超过 $K$ 段 $S_1, S_2, ..., S_K$ . 每段 $S_i$ 有字典序最大的子串 $C_i$ . 最小化 $C_i$ 的最大值. $N \le 200000$ . 分析 通过后缀数组, 先二分后缀, 再二分长度, 实现二分所有的字符串. 判定则可以贪心取, 利用后缀数组的信息, 记录 v[i] 表示位置 i 不能与位置 v[i] 在同一段中. 实现 #include <cstdio> #include

BZOJ 3277 串 (广义后缀自动机)

3277: 串 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 309 Solved: 118 [Submit][Status][Discuss] Description 字符串是oi界常考的问题.现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身). Input 第一行两个整数n,k. 接下来n行每行一个字符串. Output 输出一行n个整数,第i个整数表示第i个字符串的答案. Sa

利用后缀数组(suffix array)求最长公共子串(longest common substring)

摘要:本文讨论了最长公共子串的的相关算法的时间复杂度,然后在后缀数组的基础上提出了一个时间复杂度为o(n^2*logn),空间复杂度为o(n)的算法.该算法虽然不及动态规划和后缀树算法的复杂度低,但其重要的优势在于可以编码简单,代码易于理解,适合快速实现. 首先,来说明一下,LCS通常指的是公共最长子序列(Longest Common Subsequence,名称来源参见<算法导论>原书第3版p223),而不是公共最长子串(也称为最长公共子串). 最长公共子串问题是在文本串.模式串中寻找共有的

广义后缀自动机

1).自动机的介绍 首先我们先来介绍一下什么是自动机,有限状态自动机的功能是识别字符串,令一个自动机A,若他能识别字符串S,就记为A(S)=Ture,否则A(S)=False. 自动机由五个部分组成,alpha:字符集,state:状态集合,init:初始状态,end:结束状态集合,trans:状态转移函数. 令trans(s,ch)表示当前状态是s,在读入字符ch之后,所到达的状态.如果trans(s,ch)这个转移不存在,为了方便,设其为null,同时null只能转移到null.null表示

【BZOJ2806】【CTSC2012】Cheat 广义后缀自动机+二分+Dp

题目 题目在这里 思路&做法 我们先对标准作文库建广义后缀自动机. 然后对于每一篇阿米巴的作文, 我们首先把放到广义后缀自动机跑一遍, 对于每一个位置, 记录公共子串的长度\((\)即代码和下文中的\(val\)数组\()\) 接着我们二分答案, 用DP检验. Dp方程很好想, \(d_i = max \{ d_j + i - j \ | \ i-val_i <= j <= i-lim \}\) 可以用单点队列优化. 代码 #include <iostream> #incl

[HAOI2016]找相同字符 广义后缀自动机_统计出现次数

题目描述:给定两个字符串,求出在两个字符串中各取出一个子串使得这两个子串相同的方案数.两个方案不同当且仅当这两个子串中有一个位置不同. 输入输出格式输入格式:两行,两个字符串 s1,s2,长度分别为n1,n2.1 <=n1, n2<= 200000,字符串中只有小写字母 输出格式:输出一个整数表示答案 题解:对 $2$ 个字符串建立一个广义后缀自动机.实际上,广义后缀自动机就是对多个字符串用一个自动机加以维护.每加入完毕一个字符串时,将 $last$ 设为 $1$.插入字符时,若 $ch[la

trie上构建后缀数组

例题: 往事太多,有时候忘了就忘了吧. 如果有非记不可的,就只能用点附加手段啦! 我们定义一棵往事树是一个 n 个点 n-1 条边的有向无环图,点编号为 1到 n,其中 1 号点被称为是根结点,除根结点以外, 每个点都恰有一条出边(即以其作为起点的边). 每条边上有 1 个字符(这里我们实际上用一个不大于 300的非负整数代表不同的字符), 对于任意一个点 u,u 的所有入边(即以其为终点的边)上的字符互不相同. 接下来我们定义往事,点 u 对应的往事为 u 到根的路径(显然有且只有一条)上的所

SPOJ 705 Distinct Substrings(后缀数组)

[题目链接] http://www.spoj.com/problems/SUBST1/ [题目大意] 给出一个串,求出不相同的子串的个数. [题解] 对原串做一遍后缀数组,按照后缀的名次进行遍历, 每个后缀对答案的贡献为n-sa[i]+1-h[i], 因为排名相邻的后缀一定是公共前缀最长的, 那么就可以有效地通过LCP去除重复计算的子串. [代码] #include <cstdio> #include <cstring> #include <algorithm> usi