CF666E Forensic Examination

知识点:

原题面 Luogu


好长。


题意简述

给定字符串 \(S\)\(m\) 个模式串 \(T_1\sim T_m\)
给定 \(q\) 次询问,每次给定参数 \(p_l,p_r,l,r\),求 \(S\) 的子串 \(S[p_l:p_r]\)\(T_l\sim T_r\) 中哪个串出现次数最多,并输出出现次数。
多解输出下标最小的 \(T\)
\(1\le |S|\le 5 \times 10^5\)\(1\le m,\sum|T|\le 5\times 10^4\)\(1\le q\le 5\times 10^5\)


分析题意

多串匹配问题,考虑对 \(T_1\sim T_m\) 建立广义 SAM。
每一个状态都建立一棵线段树,将线段树的节点设为 pair 类型,维护作为 \(S[p_l:p_r]\) 对应状态时的答案。
具体地,在线构建 SAM 时在对应状态的线段树上插入 \(T\) 的下标。
线段树合并更新信息。

维护 \(S\) 的每个前缀在 SAM 中对应的状态,可通过将 \(S\) 插入 SAM 中得到。
查询时在 \(S[:p_r]\) 对应状态倍增得到 \(S[p_l:p_r]\) 对应状态。
查询对应状态线段树上 区间 \([l:r]\) 的答案即可。


爆零小技巧

傻逼错误,线段树合并 lson 写成 rson


代码实现

//知识点:广义SAM,线段树合并,倍增
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define ll long long
#define ls (lson[now_])
#define rs (rson[now_])
#define pr std::pair
#define mp std::make_pair
const int kMaxn = 6e5 + 10;
const int kMaxm = 26;
//=============================================================
char S[kMaxn], T[kMaxn];
int node_num = 1, ch[kMaxn << 1][kMaxm], len[kMaxn <<1], link[kMaxn << 1];
int edge_num, head[kMaxn << 1], v[kMaxn << 1], ne[kMaxn << 1];
int dep[kMaxn << 1], fa[kMaxn << 1][22];
int m, pos[kMaxn];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
struct SegmentTree {
  int node_num, root[kMaxn << 1], lson[kMaxn << 5], rson[kMaxn << 5];
  pr <int, int> ans[kMaxn << 5];
  void Update(int now_) {
    ans[now_] = std :: max(ans[ls], ans[rs]);
  }
  void Insert(int &now_, int L_, int R_, int pos_) {
    if (! now_) now_ = ++ node_num;
    if (L_ == R_) {
      ++ ans[now_].first;
      ans[now_].second = - L_;
      return ;
    }
    int mid = (L_ + R_) >> 1;
    if (pos_ <= mid) Insert(ls, L_, mid, pos_);
    else Insert(rs, mid + 1, R_, pos_);
    Update(now_);
  }
  int Merge(int x_, int y_, int L_, int R_) {
    if (! x_ || ! y_) return x_ + y_;
    int now_ = ++ node_num;
    if (L_ == R_) {
      ans[now_] = ans[x_];
      ans[now_].first += ans[y_].first;
      return now_;
    }
    int mid = (L_ + R_) >> 1;
    ls = Merge(lson[x_], lson[y_], L_, mid); //傻↑逼↓ (lson写成rson)
    rs = Merge(rson[x_], rson[y_], mid + 1, R_);
    Update(now_);
    return now_;
  }
  pr <int, int> Query(int now_, int L_, int R_, int ql_, int qr_) {
    if (! now_) return pr <int, int> (0, m + 1);
    if (ql_ <= L_ && R_ <= qr_) return ans[now_];
    int mid = (L_ + R_) >> 1;
    pr <int, int> ret (0, m + 1);
    if (ql_ <= mid) ret = std :: max(ret, Query(ls, L_, mid, ql_, qr_));
    if (qr_ > mid) ret = std :: max(ret, Query(rs, mid + 1, R_, ql_, qr_));
    return ret;
  }
} t; 
int Insert(int c_, int last_, int id_) {
  if (ch[last_][c_]) {
    int p = last_, q = ch[p][c_];
    if (len[p] + 1 == len[q]) {
      if (id_) t.Insert(t.root[q], 1, m, id_);
      return q;
    }
    int newq = ++ node_num;
    memcpy(ch[newq], ch[q], sizeof(ch[q])); 
    len[newq] = len[p] + 1; 
    link[newq] = link[q];
    link[q] = newq; 
    if (id_) t.Insert(t.root[newq], 1, m, id_);
    for (; p && ch[p][c_] == q; p = link[p]) ch[p][c_] = newq;
    return newq;
  }
  int p = last_, now = ++ node_num;
  len[now] = len[p] + 1;
  for (; p && ! ch[p][c_]; p = link[p]) ch[p][c_] = now;
  if (! p) {
    link[now] = 1; 
    if (id_) t.Insert(t.root[now], 1, m, id_);
    return now;
  } 
  int q = ch[p][c_];
  if (len[q] == len[p] + 1) {
    link[now] = q; 
    if (id_) t.Insert(t.root[now], 1, m, id_);
    return now;
  }
  int newq = ++ node_num;
  memcpy(ch[newq], ch[q], sizeof(ch[q])); 
  link[newq] = link[q], len[newq] = len[p] + 1; 
  link[q] = link[now] = newq;
  if (id_) t.Insert(t.root[now], 1, m, id_);
  for (; p && ch[p][c_] == q; p = link[p]) ch[p][c_] = newq;
  return now;
}
void AddEdge(int u_, int v_) {
  v[++ edge_num] = v_, ne[edge_num] = head[u_], head[u_] = edge_num;
}
void Dfs(int u_, int fat_) {
  fa[u_][0] = fat_;
  dep[u_] = dep[fat_] + 1;
  for (int i = 1; i <= 20; ++ i) {
    fa[u_][i] = fa[fa[u_][i - 1]][i - 1];
  }
  for (int i = head[u_]; i; i = ne[i]) {
    int v_ = v[i];
    Dfs(v_, u_);
    t.root[u_] = t.Merge(t.root[u_], t.root[v_], 1, m);
  }
}
int Get(int x_, int l_) {
  int ret = pos[x_];
  for (int i = 20; i >= 0; -- i) {
    if (fa[ret][i] && len[fa[ret][i]] >= l_) {
      ret = fa[ret][i];
    }
  }
  return ret;
}
void Query(int l_, int r_, int x_, int y_) {
  pr <int, int> ans = t.Query(t.root[Get(y_, y_ - x_ + 1)], 1, m, l_, r_);
  if (! ans.first) ans.second = - l_;
  printf("%d %d\n", - ans.second, ans.first);
}
//=============================================================
int main() {
  scanf("%s", S + 1);
  int n = strlen(S + 1);
  m = read();
  for (int i = 1; i <= m; ++ i) {
    scanf("%s", T + 1);
    int last = 1, nn = strlen(T + 1);
    for (int j = 1; j <= nn; ++ j) last = Insert(T[j] - 'a', last, i);
  }
  int last = 1;
  for (int i = 1; i <= n; ++ i) {
    pos[i] = last = Insert(S[i] - 'a', last, 0);
  }
  for (int i = 2; i <= node_num; ++ i) {
    AddEdge(link[i], i);
  }
  Dfs(1, 0);
  int T = read();
  while (T --) {
    int l = read(), r = read(), x = read(), y = read();
    Query(l, r, x, y);
  }
  return 0; 
}
posted @ 2020-08-23 21:51  Luckyblock  阅读(169)  评论(0编辑  收藏  举报