20201104 练习:(广义)后缀自动机

总览:

后缀自动机
广义后缀自动机
离线在 Trie 树上建立广义后缀自动机可以看作每次在线从父亲的位置开始插入一个字符
(在线做法也可以看做一个只有一条链的 Trie

T1 P6139 【模板】广义后缀自动机(广义 SAM)

思路:
模板
a n s = ∑ l e n [ i ] − l e n [ f [ i ] ] ans=\sum len[i]-len[f[i]] ans=len[i]len[f[i]]

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

#define int long long

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 2e6 + 5;
int n;
char a[A];
struct SAM {
  int ch[26];
  int len, f;
} tr[A];
int las, sz;

inline void build() {
  las = sz = 1;
  tr[1].len = tr[1].f = 0;
  for (int i = 0; i < 26; i++) tr[1].ch[i] = 0;
  return;
}

inline int insert(int x) {
  if (tr[las].ch[x]) {
    int p = las, q = tr[p].ch[x];
    if (tr[p].len + 1 == tr[q].len)
      return q;
    else {
      int cur = ++sz;
      tr[cur].len = tr[p].len + 1;
      for (int i = 0; i < 26; i++) tr[cur].ch[i] = tr[q].ch[i];
      while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
      tr[cur].f = tr[q].f, tr[q].f = cur;
      return cur;
    }
  } else {
    int p = las, cur = ++sz;
    tr[cur].len = tr[p].len + 1;
    while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
    if (!p)
      tr[cur].f = 1;
    else {
      int q = tr[p].ch[x];
      if (tr[p].len + 1 == tr[q].len)
        tr[cur].f = q;
      else {
        int cn = ++sz;
        tr[cn].len = tr[p].len + 1;
        for (int i = 0; i < 26; i++) tr[cn].ch[i] = tr[q].ch[i];
        while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
        tr[cn].f = tr[q].f, tr[cur].f = tr[q].f = cn;
      }
    }
    return cur;
  }
}

signed main() {
  n = in();
  build();
  for (int i = 1; i <= n; i++) {
    scanf("%s", a + 1);
    las = 1;
    int len = strlen(a + 1);
    for (int j = 1; j <= len; j++) las = insert(a[j] - 'a');
  }
  int ans = 0;
  for (int i = 2; i <= sz; i++) ans += tr[i].len - tr[tr[i].f].len;
  out(ans), pc('\n');
  flush();
  return 0;
}

T2 SP8093 JZPGYZ - Sevenk Love Oimaster

思路:
SAM 一个节点代表的串集合是它的子树代表的所有串的子串
转化题意成后缀树的子树包含的模式串个数
直接线段树合并

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 5e5 + 5;
const int logA = 20;
int n, Q;
char a[A];

namespace SAM {

struct Node {
  int ch[26];
  int len, f;
} tr[A];
int las, sz;

inline void clean() {
  las = sz = 1;
  tr[1].len = tr[1].f = 0;
  for (int i = 0; i < 26; i++) tr[1].ch[i] = 0;
  return;
}

inline int insert(int x) {
  if (tr[las].ch[x]) {
    int p = las, q = tr[p].ch[x];
    if (tr[q].len == tr[p].len + 1)
      return q;
    else {
      int cur = ++sz;
      tr[cur].len = tr[p].len + 1;
      for (int i = 0; i < 26; i++) tr[cur].ch[i] = tr[q].ch[i];
      while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
      tr[cur].f = tr[q].f, tr[q].f = cur;
      return cur;
    }
  }
  int p = las, cur = ++sz;
  tr[cur].len = tr[p].len + 1;
  while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
  if (!p)
    tr[cur].f = 1;
  else {
    int q = tr[p].ch[x];
    if (tr[q].len == tr[p].len + 1)
      tr[cur].f = q;
    else {
      int cn = ++sz;
      tr[cn].len = tr[p].len + 1;
      for (int i = 0; i < 26; i++) tr[cn].ch[i] = tr[q].ch[i];
      while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
      tr[cn].f = tr[q].f, tr[q].f = tr[cur].f = cn;
    }
  }
  return cur;
}

}  // namespace SAM

vector<int> t[A];
int head[A], tot_road;
struct Road {
  int nex, to;
} road[2 * A];
inline void edge(int x, int y) {
  road[++tot_road] = {head[x], y}, head[x] = tot_road;
}

struct SGT {
  int ls, rs;
  int num;
} tr[A * logA];
int rt[A], tot;

#define ls(x) tr[x].ls
#define rs(x) tr[x].rs

inline void pushup(int x) { tr[x].num = tr[ls(x)].num + tr[rs(x)].num; }

inline void insert(int &x, int l, int r, int w) {
  int y = x;
  x = ++tot;
  tr[x] = tr[y];
  if (l == r) {
    tr[x].num = 1;
    return;
  }
  int mid = (l + r) >> 1;
  if (w <= mid)
    insert(ls(x), l, mid, w);
  else
    insert(rs(x), mid + 1, r, w);
  pushup(x);
  return;
}

inline int merge(int x, int y, int l, int r) {
  if (!x || !y) return x | y;
  if (l == r) {
    tr[x].num = tr[x].num | tr[y].num;
    return x;
  }
  int mid = (l + r) >> 1;
  ls(x) = merge(ls(x), ls(y), l, mid), rs(x) = merge(rs(x), rs(y), mid + 1, r);
  pushup(x);
  return x;
}

#undef ls
#undef rs

int res[A];

inline void DFS(int x) {
  for (int y = head[x]; y; y = road[y].nex) {
    int z = road[y].to;
    DFS(z);
    rt[x] = merge(rt[x], rt[z], 1, n);
  }
  for (int i = 0; i < t[x].size(); i++) insert(rt[x], 1, n, t[x][i]);
  res[x] = tr[rt[x]].num;
  return;
}

signed main() {
  n = in(), Q = in();
  SAM::clean();
  for (int i = 1; i <= n; i++) {
    scanf("%s", a + 1);
    SAM::las = 1;
    int len = strlen(a + 1);
    for (int j = 1; j <= len; j++) {
      SAM::las = SAM::insert(a[j] - 'a');
      t[SAM::las].pb(i);
    }
  }
  for (int i = 2; i <= SAM::sz; i++) edge(SAM::tr[i].f, i);
  DFS(1);
  while (Q--) {
    scanf("%s", a + 1);
    int len = strlen(a + 1);
    int p = 1, pos = 0;
    for (int i = 1; i <= len; i++) {
      if (!SAM::tr[p].ch[a[i] - 'a']) {
        pos = 1;
        break;
      }
      p = SAM::tr[p].ch[a[i] - 'a'];
    }
    if (pos)
      out("0\n");
    else
      out(res[p]), pc('\n');
  }
  flush();
  return 0;
}

T3 P6640 [BJOI2020] 封印

思路:
即求 t t t s [ l , l ] − s [ l , r ] s[l,l]-s[l,r] s[l,l]s[l,r] 的最长公共后缀长度
预处理 t t t s [ 1 , k ] s[1,k] s[1,k] 的最长公共后缀长度 v a l i val_i vali
即求 m a x i = l r ( m i n ( i − l + 1 , v a l i ) ) max_{i=l}^r(min(i-l+1,val_i)) maxi=lr(min(il+1,vali))
发现 i − l + 1 i-l+1 il+1 i i i 右移的过程中每次加一, v a l i val_i vali 每次加一或清零
所以 f ( i ) = ( i − l + 1 ) − v a l i f(i)=(i-l+1)-val_i f(i)=(il+1)vali 单增
二分零点,零点左边取 w − l + 1 w-l+1 wl+1,右边取 m a x ( v a l i ) max(val_i) max(vali)
再用 S T ST ST 表维护区间最大即可

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 5e5 + 5;
const int logA = 21;
char ss[A], tt[A];
int lg[A], val[A], st[A][logA];

struct SAM {
  int las, sz;
  int ch[A][26], len[A], f[A];

  inline void clean() {
    las = sz = 1;
    len[1] = f[1] = 0;
    for (int i = 0; i < 26; i++) ch[1][i] = 0;
    return;
  }

  inline int insert(int x) {
    int p = las, cur = ++sz;
    len[cur] = len[p] + 1;
    while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
    if (!p)
      f[cur] = 1;
    else {
      int q = ch[p][x];
      if (len[q] == len[p] + 1)
        f[cur] = q;
      else {
        int cn = ++sz;
        len[cn] = len[p] + 1;
        for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
        while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
        f[cn] = f[q], f[q] = f[cur] = cn;
      }
    }
    return cur;
  }

  inline void build() {
    clean();
    int kk = strlen(tt + 1);
    for (int i = 1; i <= kk; i++) las = insert(tt[i] - 'a');
    return;
  }

  inline void prepare() {
    int kk = strlen(ss + 1);
    int p = 1, ans = 0;
    for (int i = 1; i <= kk; i++) {
      while (p && !ch[p][ss[i] - 'a']) p = f[p], ans = len[p];
      if (!p) p = 1, ans = len[p];
      if (ch[p][ss[i] - 'a']) {
        p = ch[p][ss[i] - 'a'];
        ans++;
      }
      val[i] = ans;
    }
    for (int i = 2; i <= kk; i++) lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i <= kk; i++) st[i][0] = val[i];
    for (int i = 1; i <= lg[kk]; i++)
      for (int j = 1; j + (1 << i) - 1 <= kk; j++)
        st[j][i] = max(st[j][i - 1], st[j + (1 << (i - 1))][i - 1]);
    return;
  }

} S;

inline int find(int l, int r) {
  int k = lg[r - l + 1];
  return max(st[l][k], st[r - (1 << k) + 1][k]);
}

inline int query(int l, int r) {
  int L = l, R = r, ans = l - 1;
  while (L <= R) {
    int mid = (L + R) >> 1;
    if ((mid - l + 1) - val[mid] <= 0)
      L = mid + 1, ans = mid;
    else
      R = mid - 1;
  }
  return ans < r ? max(ans - l + 1, find(ans + 1, r)) : ans - l + 1;
}

signed main() {
  scanf("%s%s", ss + 1, tt + 1);
  S.build();
  S.prepare();
  int Q = in();
  while (Q--) {
    int l = in(), r = in();
    out(query(l, r)), pc('\n');
  }
  flush();
  return 0;
}

T4 P3346 [ZJOI2015]诸神眷顾的幻想乡

思路:
叶子节点数小于等于十
于是以每个叶子节点为根,遍历整棵树,插入 SAM
这样的 SAM 中必定包含所有子串
最后统计答案即可

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(LL x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 3e6 + 5;
int n, m;

int head[A], tot_road;
struct Road {
  int nex, to;
} road[2 * A];
inline void edge(int x, int y) {
  road[++tot_road] = {head[x], y}, head[x] = tot_road;
}
int rd[A], col[A];
int pt[A];

struct SAM {
  int ch[20];
  int len, f;
} tr[A];
int sz;

inline void clean() {
  sz = 1;
  tr[1].len = tr[1].f = 0;
  for (int i = 0; i < m; i++) tr[1].ch[i] = 0;
  return;
}

inline int insert(int x, int las) {
  if (tr[las].ch[x]) {
    int p = las, q = tr[las].ch[x];
    if (tr[p].len + 1 == tr[q].len)
      return q;
    else {
      int cur = ++sz;
      tr[cur].len = tr[p].len + 1;
      for (int i = 0; i < m; i++) tr[cur].ch[i] = tr[q].ch[i];
      while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
      tr[cur].f = tr[q].f, tr[q].f = cur;
      return cur;
    }
  }
  int p = las, cur = ++sz;
  tr[cur].len = tr[p].len + 1;
  while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
  if (!p)
    tr[cur].f = 1;
  else {
    int q = tr[p].ch[x];
    if (tr[p].len + 1 == tr[q].len)
      tr[cur].f = q;
    else {
      int cn = ++sz;
      tr[cn].len = tr[p].len + 1;
      for (int i = 0; i < m; i++) tr[cn].ch[i] = tr[q].ch[i];
      while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
      tr[cn].f = tr[q].f, tr[q].f = tr[cur].f = cn;
    }
  }
  return cur;
}

inline void DFS(int fa, int x) {
  pt[x] = insert(col[x], pt[fa]);
  for (int y = head[x]; y; y = road[y].nex) {
    int z = road[y].to;
    if (z == fa) continue;
    DFS(x, z);
  }
  return;
}

signed main() {
  n = in(), m = in();
  for (int i = 1; i <= n; i++) col[i] = in();
  for (int i = 1; i < n; i++) {
    int u = in(), v = in();
    edge(u, v), edge(v, u);
    rd[u]++, rd[v]++;
  }
  clean();
  pt[0] = 1;
  for (int i = 1; i <= n; i++)
    if (rd[i] == 1) DFS(0, i);
  LL ans = 0;
  for (int i = 2; i <= sz; i++) ans += tr[i].len - tr[tr[i].f].len;
  out(ans), pc('\n');
  flush();
  return 0;
}

T5 CF427D Match & Catch

思路:
SAM 中从叶子节点向上跳,节点代表的串的出现次数每次加一
所以一个串在 SAM 中的出现次数为子树大小
建立广义后缀自动机,对每个结点分别统计在两个串中的出现次数
一个节点代表的最小值子串长度为父亲的 l e n + 1 len+1 len+1
用出现次数都为一的点更新答案

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 5e5 + 5;
const int INF = 1e9;
char a[A];

struct SAM {
  int ch[26];
  int len, f;
} tr[A];
int las, sz;

inline void build() {
  las = sz = 1;
  tr[1].len = tr[1].f = 0;
  for (int i = 0; i < 26; i++) tr[1].ch[i] = 0;
  return;
}

inline int insert(int x) {
  if (tr[las].ch[x]) {
    int p = las, q = tr[p].ch[x];
    if (tr[q].len == tr[p].len + 1)
      return q;
    else {
      int cur = ++sz;
      tr[cur].len = tr[p].len + 1;
      for (int i = 0; i < 26; i++) tr[cur].ch[i] = tr[q].ch[i];
      while (p && tr[p].ch[x] == q) tr[p].ch[x] = cur, p = tr[p].f;
      tr[cur].f = tr[q].f, tr[q].f = cur;
      return cur;
    }
  } else {
    int p = las, cur = ++sz;
    tr[cur].len = tr[p].len + 1;
    while (p && !tr[p].ch[x]) tr[p].ch[x] = cur, p = tr[p].f;
    if (!p)
      tr[cur].f = 1;
    else {
      int q = tr[p].ch[x];
      if (tr[q].len == tr[p].len + 1)
        tr[cur].f = q;
      else {
        int cn = ++sz;
        tr[cn].len = tr[p].len + 1;
        for (int i = 0; i < 26; i++) tr[cn].ch[i] = tr[q].ch[i];
        while (p && tr[p].ch[x] == q) tr[p].ch[x] = cn, p = tr[p].f;
        tr[cn].f = tr[q].f, tr[cur].f = tr[q].f = cn;
      }
    }
    return cur;
  }
}

int num[2][A];

int t[A], tmp[A];

inline void topo() {
  for (int i = 1; i <= sz; i++) tmp[tr[i].len]++;
  for (int i = 1; i <= sz; i++) tmp[i] += tmp[i - 1];
  for (int i = 1; i <= sz; i++) t[tmp[tr[i].len]--] = i;
  return;
}

signed main() {
  build();
  scanf("%s", a + 1);
  int len = strlen(a + 1);
  for (int i = 1; i <= len; i++) las = insert(a[i] - 'a'), num[0][las] = 1;
  scanf("%s", a + 1);
  len = strlen(a + 1);
  las = 1;
  for (int i = 1; i <= len; i++) las = insert(a[i] - 'a'), num[1][las] = 1;
  topo();
  for (int i = sz; i; i--)
    num[0][tr[t[i]].f] += num[0][t[i]], num[1][tr[t[i]].f] += num[1][t[i]];
  int ans = INF;
  for (int i = 2; i <= sz; i++)
    if (num[0][i] == 1 && num[1][i] == 1) ans = min(ans, tr[tr[i].f].len + 1);
  if (ans == INF)
    out("-1\n");
  else
    out(ans), pc('\n');
  flush();
  return 0;
}

T6 CF452E Three strings

思路:
建立广义 SAM,对每个结点统计在三个串中的出现次数,每个节点对答案的贡献为出现次数的积,用每个节点区间更新答案,差分维护

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

#define int long long

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  for (; x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 6e5 + 5;
const int mod = 1e9 + 7;
const int INF = 1e9;
inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
inline int dec(int x, int y) { return x - y < 0 ? x - y + mod : x - y; }
inline int mul(int x, int y) {
  return x * y % mod < 0 ? x * y % mod + mod : x * y % mod;
}
inline void Add(int &x, int y) { x = add(x, y); }
inline void Dec(int &x, int y) { x = dec(x, y); }
inline void Mul(int &x, int y) { x = mul(x, y); }
char a[A];

struct SAM {
  int n = INF;
  int las, sz;
  int ch[A][26], len[A], f[A];

  inline void clean() {
    las = sz = 1;
    len[1] = f[1] = 0;
    for (int i = 0; i < 26; i++) ch[1][i] = 0;
    return;
  }

  inline int insert(int x) {
    if (ch[las][x]) {
      int p = las, q = ch[p][x];
      if (len[p] + 1 == len[q])
        return q;
      else {
        int cur = ++sz;
        len[cur] = len[p] + 1;
        for (int i = 0; i < 26; i++) ch[cur][i] = ch[q][i];
        while (p && ch[p][x] == q) ch[p][x] = cur, p = f[p];
        f[cur] = f[q], f[q] = cur;
        return cur;
      }
    } else {
      int p = las, cur = ++sz;
      len[cur] = len[p] + 1;
      while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
      if (!p)
        f[cur] = 1;
      else {
        int q = ch[p][x];
        if (len[q] == len[p] + 1)
          f[cur] = q;
        else {
          int cn = ++sz;
          len[cn] = len[p] + 1;
          for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
          while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
          f[cn] = f[q], f[q] = f[cur] = cn;
        }
      }
      return cur;
    }
  }

  int num[3][A];

  inline void build(int now) {
    las = 1;
    int len = strlen(a + 1);
    n = min(n, len);
    for (int i = 1; i <= len; i++) las = insert(a[i] - 'a'), num[now][las]++;
    return;
  }

  int c[A], t[A];

  inline void topo() {
    for (int i = 1; i <= sz; i++) t[len[i]]++;
    for (int i = 1; i <= sz; i++) t[i] += t[i - 1];
    for (int i = 1; i <= sz; i++) c[t[len[i]]--] = i;
    return;
  }

  int res[A];

  inline void query() {
    topo();
    for (int i = sz; i; i--)
      for (int j = 0; j < 3; j++) num[j][f[c[i]]] += num[j][c[i]];
    for (int i = 2; i <= sz; i++)
      Add(res[len[f[i]] + 1], mul(mul(num[0][i], num[1][i]), num[2][i])),
          Dec(res[len[i] + 1], mul(mul(num[0][i], num[1][i]), num[2][i]));
    for (int i = 1; i <= n; i++) Add(res[i], res[i - 1]), out(res[i]), pc(' ');
    return;
  }

} S;

signed main() {
  S.clean();
  for (int i = 0; i < 3; i++) {
    scanf("%s", a + 1);
    S.build(i);
  }
  S.query(), pc('\n');
  flush();
  return 0;
}

T7 CF235C Cyclical Quest

思路:
循环同构即每次删除首字母,然后在尾部插入一个字母
对于删除首字母可以跳 fail树,直到匹配长度小于删除后的字符串长度
建立 SAM 匹配即可

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 2e6 + 5;
char a[A];

struct SAM {
  int ch[A][26], f[A], len[A], num[A];
  int sz, las;

  inline int insert(int x) {
    int p = las, cur = ++sz;
    len[cur] = len[p] + 1;
    while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
    if (!p)
      f[cur] = 1;
    else {
      int q = ch[p][x];
      if (len[q] == len[p] + 1)
        f[cur] = q;
      else {
        int cn = ++sz;
        len[cn] = len[p] + 1;
        for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
        while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
        f[cn] = f[q], f[q] = f[cur] = cn;
      }
    }
    return cur;
  }

  inline void clean() {
    sz = las = 1;
    f[1] = len[1] = 0;
    for (int i = 0; i < 26; i++) ch[1][i] = 0;
    return;
  }

  int t[A], c[A];

  inline void topo() {
    for (int i = 1; i <= sz; i++) t[len[i]]++;
    for (int i = 1; i <= sz; i++) t[i] += t[i - 1];
    for (int i = 1; i <= sz; i++) c[t[len[i]]--] = i;
    return;
  }

  inline void build() {
    clean();
    int L = strlen(a + 1);
    for (int i = 1; i <= L; i++) las = insert(a[i] - 'a'), num[las] = 1;
    topo();
    for (int i = sz; i; i--) num[f[c[i]]] += num[c[i]];
    return;
  }

  int ex[A], st[A], top;
  int ans;

  inline int query() {
    top = 0, ans = 0;
    int L = strlen(a + 1), now = 0;
    int p = 1;
    for (int i = 1; i <= L; i++) {
      while (p && !ch[p][a[i] - 'a']) p = f[p], now = len[p];
      if (!p) p = 1, now = 0;
      if (ch[p][a[i] - 'a']) p = ch[p][a[i] - 'a'], now++;
    }
    if (!ex[p] && now == L) {
      ans += num[p];
      ex[p] = 1;
      st[++top] = p;
      while (p && len[f[p]] + 1 >= L) p = f[p];
      now--;
    }
    for (int i = 1; i < L; i++) {
      while (p && !ch[p][a[i] - 'a']) p = f[p], now = len[p];
      if (!p) p = 1, now = 0;
      if (ch[p][a[i] - 'a']) p = ch[p][a[i] - 'a'], now++;
      if (!ex[p] && now == L) {
        ans += num[p];
        ex[p] = 1;
        st[++top] = p;
        while (p && len[f[p]] + 1 >= L) p = f[p];
        now--;
      }
    }
    for (int i = 1; i <= top; i++) ex[st[i]] = 0;
    return ans;
  }

} S;

signed main() {
  scanf("%s", a + 1);
  S.build();
  int Q = in();
  while (Q--) {
    scanf("%s", a + 1);
    out(S.query()), pc('\n');
  }
  flush();
  return 0;
}

T8 P4022 [CTSC2012]熟悉的文章

思路:
对于每篇作文处理
直接求 L 0 L_0 L0 不好求,于是二分答案
考虑怎么判断对于每个位置预处理出开头到这个位置的后缀最大匹配长度
dp方程
f i = m a x ( f i − 1 , f j + i − j ) ( j ∈ [ i − m a x l e n , i − L 0 ] ) f_i=max(f_{i-1},f_j+i-j) (j\in [i-maxlen,i-L_0]) fi=max(fi1,fj+ij)(j[imaxlen,iL0])
m a x l e n maxlen maxlen 为开头到这个位置的后缀最大匹配长度
发现 i − m a x l e n i-maxlen imaxlen 不降,于是可以单调队列优化成 O ( n ) O(n) O(n)

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (re int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (re int i = 0; i < len; ++i) pc(x[i]);
}
}  // namespace IO
using namespace IO;

const int A = 2e6 + 5;
int n, m;
char a[A];
int mx[A], f[A];
int q[A], ql, qr;

inline int check(int now) {
  int L = strlen(a + 1);
  for (re int i = 1; i <= L; ++i) f[i] = 0;
  ql = 1, qr = 0;
  for (re int i = 1; i <= L; ++i) {
    while (ql <= qr && q[ql] < i - mx[i]) ql++;
    f[i] = f[i - 1];
    if (ql <= qr) f[i] = max(f[i], f[q[ql]] + i - q[ql]);
    if (i - now + 1 >= 0) {
      while (ql <= qr && f[i - now + 1] - (i - now + 1) >= f[q[qr]] - q[qr])
        qr--;
      q[++qr] = i - now + 1;
    }
  }
  if (f[L] >= (int)ceil(0.9 * L)) return 1;
  return 0;
}

inline void solve() {
  int L = 0, R = strlen(a + 1), ans = 0;
  while (L <= R) {
    int mid = (L + R) >> 1;
    if (check(mid))
      L = mid + 1, ans = mid;
    else
      R = mid - 1;
  }
  out(ans), pc('\n');
  return;
}

struct SAM {
  int ch[A][2], f[A], len[A];
  int sz;

  inline void rebuild() {
    sz = 1;
    f[1] = len[1] = 0;
    for (re int i = 0; i < 2; ++i) ch[1][i] = 0;
    return;
  }

  inline int insert(int x, int las) {
    if (ch[las][x]) {
      int p = las, q = ch[las][x];
      if (len[p] + 1 == len[q])
        return q;
      else {
        int cur = ++sz;
        len[cur] = len[p] + 1;
        for (re int i = 0; i < 2; ++i) ch[cur][i] = ch[q][i];
        while (p && ch[p][x] == q) ch[p][x] = cur, p = f[p];
        f[cur] = f[q], f[q] = cur;
        return cur;
      }
    } else {
      int p = las, cur = ++sz;
      len[cur] = len[p] + 1;
      while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
      if (!p)
        f[cur] = 1;
      else {
        int q = ch[p][x];
        if (len[p] + 1 == len[q])
          f[cur] = q;
        else {
          int cn = ++sz;
          len[cn] = len[p] + 1;
          for (re int i = 0; i < 2; ++i) ch[cn][i] = ch[q][i];
          while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
          f[cn] = f[q], f[q] = f[cur] = cn;
        }
      }
      return cur;
    }
  }

  inline void add() {
    int las = 1;
    int L = strlen(a + 1);
    for (re int i = 1; i <= L; ++i) las = insert(a[i] - '0', las);
    return;
  }

  inline void prepare() {
    int L = strlen(a + 1), p = 1, res = 0;
    for (re int i = 1; i <= L; ++i) {
      int x = a[i] - '0';
      while (p && !ch[p][x]) p = f[p], res = len[p];
      if (!p)
        p = 1, res = 0;
      else
        p = ch[p][x], res++;
      mx[i] = res;
    }
    return;
  }

} T;

signed main() {
  n = in(), m = in();
  T.rebuild();
  while (m--) {
    scanf("%s", a + 1);
    T.add();
  }
  while (n--) {
    scanf("%s", a + 1);
    T.prepare();
    solve();
  }
  flush();
  return 0;
}

T9 P5212 SubString

思路:
动态加点,求子树和
LCT 维护
每次链加,增加辅助节点要从复制的节点处继承信息

代码:

#include <bits/stdc++.h>
using namespace std;

#define re register
#define LL long long
typedef unsigned int uint;
typedef unsigned long long ull;
#define fir first
#define sec second
#define pb push_back
#define mp make_pair

namespace IO {
char buf_[1 << 21], *p1_ = buf_, *p2_ = buf_;
#define ch()                                                                 \
  (p1_ == p2_ &&                                                             \
           (p2_ = (p1_ = buf_) + fread(buf_, 1, 1 << 21, stdin), p1_ == p2_) \
       ? EOF                                                                 \
       : *p1_++)
inline int in() {
  int s = 0, f = 1;
  char x = getchar();
  for (; x < '0' || x > '9'; x = getchar())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = getchar()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}
char _buf[1 << 21];
int _pos = -1;
inline void flush() { fwrite(_buf, 1, _pos + 1, stdout), _pos = -1; }
inline void pc(char x) {
  if (_pos == (1 << 21) - 1) flush();
  _buf[++_pos] = x;
}
inline void out(int x) {
  char k[30];
  int pos = 0;
  if (!x) return pc('0');
  if (x < 0) pc('-'), x = -x;
  while (x) k[++pos] = (x % 10) | 48, x /= 10;
  for (re int i = pos; i; i--) pc(k[i]);
}
inline void out(string x) {
  int len = x.size();
  for (re int i = 0; i < len; ++i) pc(x[i]);
}
}  // namespace IO
using namespace IO;

string decodeWithMask(string s, int mask) {
  for (int j = 0; j < s.size(); j++) {
    mask = (mask * 131 + j) % s.size();
    char t = s[j];
    s[j] = s[mask];
    s[mask] = t;
  }
  return s;
}

const int A = 1e7 + 5;
string a;
int mk = 0;

struct LCT {
  int ch[A][2], f[A], rev[A], val[A], tag[A];

  inline int isroot(int x) { return ch[f[x]][0] != x && ch[f[x]][1] != x; }

  inline void reverse(int x) {
    if (x) swap(ch[x][0], ch[x][1]), rev[x] ^= 1;
  }

  inline void pushdown(int x) {
    if (rev[x]) reverse(ch[x][0]), reverse(ch[x][1]), rev[x] ^= 1;
    if (tag[x]) {
      if (ch[x][0]) {
        val[ch[x][0]] += tag[x];
        tag[ch[x][0]] += tag[x];
      }
      if (ch[x][1]) {
        val[ch[x][1]] += tag[x];
        tag[ch[x][1]] += tag[x];
      }
      tag[x] = 0;
    }
    return;
  }

  inline void rotate(int x) {
    int y = f[x], z = f[y];
    int k = (ch[y][1] == x);
    if (z && !isroot(y)) ch[z][(ch[z][1] == y)] = x;
    f[x] = z, ch[y][k] = ch[x][k ^ 1];
    if (ch[x][k ^ 1]) f[ch[x][k ^ 1]] = y;
    ch[x][k ^ 1] = y, f[y] = x;
    return;
  }

  int st[A], top;
  inline void pushpath(int x) {
    top = 0;
    st[++top] = x;
    for (int i = x; !isroot(i); i = f[i]) st[++top] = f[i];
    for (int i = top; i; i--) {
      pushdown(st[i]);
    }
    return;
  }

  inline void splay(int x) {
    pushpath(x);
    while (!isroot(x)) {
      int y = f[x], z = f[y];
      if (!isroot(y)) {
        if ((ch[z][1] == y) == (ch[y][1] == x))
          rotate(y);
        else
          rotate(x);
      }
      rotate(x);
    }
    return;
  }

  inline void access(int x) {
    for (int y = 0; x; y = x, x = f[x]) splay(x), ch[x][1] = y;
  }

  inline int findroot(int x) {
    access(x), splay(x);
    while (ch[x][0]) pushdown(x), x = ch[x][0];
    return x;
  }

  inline void makeroot(int x) { access(x), splay(x), reverse(x); }

  inline void split(int x, int y) { makeroot(x), access(y), splay(y); }

  inline void link(int x, int y) {
    makeroot(x);
    if (findroot(y) != x) f[x] = y;
  }

  inline void cut(int x, int y) {
    makeroot(x);
    if (findroot(y) == x && f[x] == y && ch[y][0] == x) f[x] = 0, ch[y][0] = 0;
  }

  inline void modify(int x, int y, int w) {
    split(x, y);
    val[y] += w, tag[y] += w;
  }

  inline int query(int x) {
    splay(x);
    return val[x];
  }

} T;

struct SAM {
  int ch[A][26], len[A], f[A];
  int sz, las;

  inline void build() {
    sz = las = 1;
    len[1] = f[1] = 0;
    for (int i = 0; i < 26; i++) ch[1][i] = 0;
    return;
  }

  inline int insert(int x) {
    int p = las, cur = ++sz;
    len[cur] = len[p] + 1;
    while (p && !ch[p][x]) ch[p][x] = cur, p = f[p];
    if (!p)
      f[cur] = 1, T.link(1, cur);
    else {
      int q = ch[p][x];
      if (len[q] == len[p] + 1)
        f[cur] = q, T.link(q, cur);
      else {
        int cn = ++sz;
        len[cn] = len[p] + 1;
        for (int i = 0; i < 26; i++) ch[cn][i] = ch[q][i];
        while (p && ch[p][x] == q) ch[p][x] = cn, p = f[p];
        if (f[q]) T.cut(q, f[q]);
        f[cn] = f[q], f[q] = f[cur] = cn;
        if (f[cn]) T.link(f[cn], cn);
        T.link(cn, q), T.link(cn, cur);
        T.val[cn] = T.val[q];
      }
    }
    return cur;
  }

  inline void insert() {
    int L = a.size();
    for (int i = 0; i < L; i++) {
      las = insert(a[i] - 'A');
      T.modify(1, las, 1);
    }
    return;
  }

  inline int query() {
    int p = 1, L = a.size();
    for (int i = 0; i < L; i++) {
      int x = a[i] - 'A';
      if (!ch[p][x]) return 0;
      p = ch[p][x];
    }
    return T.query(p);
  }

} S;

signed main() {
  int Q = in();
  S.build();
  cin >> a;
  S.insert();
  while (Q--) {
    cin >> a;
    if (a == "ADD") {
      cin >> a;
      a = decodeWithMask(a, mk);
      S.insert();
    } else {
      cin >> a;
      a = decodeWithMask(a, mk);
      int now = S.query();
      mk ^= now;
      out(now), pc('\n');
    }
  }
  flush();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值