20200908 練習:(廣義)字尾自動機

ハルカナソラヘ發表於2020-11-04

總覽:

字尾自動機
廣義字尾自動機
離線在 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;
}

相關文章