后缀自动机 线段树合并 codeforces666E
Posted foreverpiano
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀自动机 线段树合并 codeforces666E相关的知识,希望对你有一定的参考价值。
http://codeforces.com/problemset/problem/666/E
首先一个显然的想法
对于广义后缀自动机上每一个点
开线段树存子树出现次数的众数
预处理可以使用线段树合并
问题在于询问\([l, r]\)的时候
如何找到点\(r\)的位置(此时左端点是从\(l\)出发的,而不是从\(1\)出发)
其实\([1, r]\)匹配的位置\(u\)和\([l, r]\)匹配到的位置\(v\)
有在后缀树上 \(v\) 是 \(u\) 的祖先的结论
于是使用倍增找到\(v\)然后线段树询问子树就好了
复杂度\(O((L + Q) \log L), L = Len_s + \sum T_i\)
也不是很难写吗QAQ
#include <bits/stdc++.h>
#define fo(i, n) for(int i = 1; i <= (n); i ++)
#define out(x) cerr << #x << " = " << x << "\n"
#define type(x) __typeof((x).begin())
#define foreach(it, x) for(type(x) it = (x).begin(); it != (x).end(); ++ it)
using namespace std;
// by piano
template<typename tp> inline void read(tp &x) {
x = 0;char c = getchar(); bool f = 0;
for(; c < '0' || c > '9'; f |= (c == '-'), c = getchar());
for(; c >= '0' && c <= '9'; x = (x << 3) + (x << 1) + c - '0', c = getchar());
if(f) x = -x;
}
template<typename tp> inline void arr(tp *a, int n) {
for(int i = 1; i <= n; i ++)
cout << a[i] << " ";
puts("");
}
const int N = 5e5 + 233;
const int SamN = (6e5 + 233) * 2;
const int SZ = 26;
const int lgN = 20;
int n, m, Q, len;
int pos[N], rt[SamN];
char s[N], t[N];
namespace bit {
#define mid (l + (r - l) / 2)
const int SG = 7e6 + 233;
struct Node {
int Max, id;
Node(int _Max = 0, int _id = 0) {
Max = _Max; id = _id;
}
bool operator < (const Node &rhs) const {
return Max < rhs.Max || Max == rhs.Max && id > rhs.id;
}
}tr[SG];
int L[SG], R[SG], tot = 0;
inline void ps(int rt) {
if(!L[rt]) tr[rt] = tr[R[rt]];
else if(!R[rt]) tr[rt] = tr[L[rt]];
else tr[rt] = max(tr[L[rt]], tr[R[rt]]);
}
inline void add(int &rt, int l, int r, int pos, int val) {
if(!rt) rt = ++ tot;
if(l == r) {
tr[rt].id = l;
tr[rt].Max ++;
return ;
}
if(pos <= mid) add(L[rt], l, mid, pos, val);
else add(R[rt], mid + 1, r, pos, val);
ps(rt);
}
inline Node query(int rt, int l, int r, int ql, int qr) {
if(!rt) return (Node) {-233, 0};
if(ql == l && qr == r) return tr[rt];
if(qr <= mid) return query(L[rt], l, mid, ql, qr);
else if(ql > mid) return query(R[rt], mid + 1, r, ql, qr);
else return max(query(L[rt], l, mid, ql, mid), query(R[rt], mid + 1, r, mid + 1, qr));
}
inline int uni(int rt, int pre, int l, int r) {
if(!rt || !pre) return rt | pre;
int pre_rt = rt;
rt = ++ tot;
if(l == r) {
tr[rt].Max = tr[pre_rt].Max + tr[pre].Max;
tr[rt].id = l;
return rt;
}
L[rt] = uni(L[pre_rt], L[pre], l, mid);
R[rt] = uni(R[pre_rt], R[pre], mid + 1, r);
ps(rt);
return rt;
}
#undef mid
}
struct Sam {
int sa, last, root;
int son[SamN][SZ], pre[SamN], step[SamN];
int st[SamN], a[SamN];
int fa[SamN][lgN + 2];
inline void init(void) {
sa = 0;
last = root = ++ sa;
}
inline int nw(int val) {
++ sa; step[sa] = val; return sa;
}
inline void ins(char ch, int id, int type) {
if(son[last][ch] && step[son[last][ch]] == step[last] + 1) {
int np = last = son[last][ch];
if(type)
bit::add(rt[np], 1, n, id, 1);
return ;
}
int p = last, np = last = nw(step[p] + 1);
if(type)
bit::add(rt[np], 1, n, id, 1);
else pos[id] = np;
for(; !son[p][ch]; p = pre[p]) son[p][ch] = np;
if(!p) pre[np] = root;
else {
int q = son[p][ch], nq;
if(step[q] != step[p] + 1) {
nq = nw(step[p] + 1);
memcpy(son[nq], son[q], sizeof son[q]);
pre[nq] = pre[q]; pre[q] = pre[np] = nq;
for(; son[p][ch] == q; p = pre[p]) son[p][ch] = nq;
}
else pre[np] = q;
}
}
inline void bit_init(void) {
fo(i, sa) st[step[i]] ++;
fo(i, sa) st[i] += st[i - 1];
fo(i, sa) a[st[step[i]] --] = i;
for(int i = sa; i >= 1; i --) {
int x = a[i];
if(pre[x])
rt[pre[x]] = bit::uni(rt[pre[x]], rt[x], 1, n);
}
}
inline void lca_init(void) {
for(int i = 1; i <= sa; i ++) fa[i][0] = pre[i];
for(int i = 1; i <= lgN; i ++)
for(int j = 1; j <= sa; j ++)
fa[j][i] = fa[fa[j][i - 1]][i - 1];
}
inline int getpos(int x, int len) {
for(int i = lgN; i >= 0; i --)
if(step[fa[x][i]] >= len)
x = fa[x][i];
return x;
}
}sam;
inline void doit(int u, int l, int r) {
bit::Node ans = bit::query(rt[u], 1, n, l, r);
if(ans.Max == -233) ans.Max = 0, ans.id = l;
cout << ans.id << " " << ans.Max << "\n";
}
main(void) {
sam.init();
scanf("%s", s + 1);
len = strlen(s + 1);
for(int k = 1; k <= len; k ++) sam.ins(s[k] - 'a', k, 0);
sam.last = sam.root;
read(n);
for(int i = 1; i <= n; i ++) {
scanf("%s", t + 1);
m = strlen(t + 1);
for(int k = 1; k <= m; k ++) sam.ins(t[k] - 'a', i, 1);
sam.last = sam.root;
}
sam.lca_init(); sam.bit_init();
for(read(Q); Q --;) {
int l, r, ql, qr;
read(ql); read(qr); read(l); read(r);
int t = sam.getpos(pos[r], r - l + 1);
doit(t, ql, qr);
}
}
以上是关于后缀自动机 线段树合并 codeforces666E的主要内容,如果未能解决你的问题,请参考以下文章
CF.666E.Forensic Examination(广义后缀自动机 线段树合并)
CF666E Forensic Examination [后缀自动机,线段树合并]
CF666E Forensic Examination(广义后缀自动机+线段树合并)
CF666E Forensic Examination(后缀自动机+线段树合并)