SP705 SUBST1 - New Distinct Substrings题解

传送门

来交一个广义后缀自动机的题解。

这题需要用到一个后缀自动机的性质:设 \(minlen[i]\) 表示 \(i\) 节点所表示的 \(endpos\) 等价类中最短串的长度, \(len[i]\) 表示 \(i\) 节点所表示的 \(endpos\) 等价类中最长串的长度, \(fa[i]\) 表示 \(i\) 节点在 parent tree 上的父亲,则:

\[\begin{aligned} minlen[i]=len[fa[i]]+1 \end{aligned} \]

有了这个结论,这题就有解决方法了,一个 \(endpos\) 等价类 \(i\) 中本质不同的子串个数为 \(len[i]-minlen[i]+1\) ,并且不同的 \(endpos\) 等价类中所含子串一定不同,所以遍历所有 \(endpos\) 等价类,统计答案即可。

代码

// Problem: SP705 SUBST1 - New Distinct Substrings
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/SP705
// Memory Limit: 1500 MB
// Time Limit: 280 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define int long long
using namespace std;
namespace Std {
int k, n, ans;
char s[50010];
int fa[100010], len[100010], tot = 1, lst = 1, son[100010][26];
template <typename tp>
void read(tp &x);
template <typename tp>
void write(tp x);
void insert(int x);
void add(int x, int y);
void dfs(int x);
template <typename tp>
void read(tp &x) {
  x = 0;
  tp nev = 1;
  char c = getchar();
  while (c < '0' || c > '9') {
    if (c == '-') nev = -1;
    c = getchar();
  }
  while (c >= '0' && c <= '9') {
    x = (x << 1) + (x << 3) + (c ^ 48);
    c = getchar();
  }
  x *= nev;
}
template <typename tp>
void write(tp x) {
  if (x < 0) {
    putchar('-');
    write(-x);
    return;
  }
  if (x / 10) write(x / 10);
  putchar((x % 10) ^ 48);
}
void insert(int x) {
  int p = lst, np = lst = ++tot;
  len[np] = len[p] + 1;
  memset(son[np], 0, sizeof(son[np]));
  while (p && (!son[p][x])) {
    son[p][x] = np;
    p = fa[p];
  }
  if (!p)
    fa[np] = 1;
  else {
    int q = son[p][x];
    if (len[q] == len[p] + 1)
      fa[np] = q;
    else {
      int nq = ++tot;
      memcpy(son[nq], son[q], sizeof(son[nq]));
      fa[nq] = fa[q];
      len[nq] = len[p] + 1;
      fa[q] = fa[np] = nq;
      while (p && son[p][x] == q) {
        son[p][x] = nq;
        p = fa[p];
      }
    }
  }
}
int main(void) {
  read(k);
  while (k--) {
    memset(son[1], 0, sizeof(son[1]));
    scanf("%s", s + 1);
    n = strlen(s + 1);
    tot = lst = 1;
    for (int i = 1; i <= n; ++i) insert(s[i] - 'a');
    ans = 0;
    for (int i = 2; i <= tot; ++i) {
      ans += len[i] - len[fa[i]];
    }
    printf("%lld\n", ans);
  }
  return 0;
}
}  // namespace Std
#undef int
int main(int argc, char *argv[]) { return Std::main(); }
posted @ 2022-05-09 21:06  Wilson_Inversion  阅读(12)  评论(0编辑  收藏  举报