后缀自动机 线段树合并 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(后缀自动机+线段树合并)

Codeforces 666E Forensic Examination SAM+权值线段树

P5161 WD与数列(后缀自动机+线段树合并)