[bzoj4516][Sdoi2016]生成魔咒——后缀自动机

Brief Description

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。
一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒。
例如 S=[1,2,1] 时,它的生成魔咒有 [1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。S=[1,1,1] 时,它的生成魔咒有 [1]、
[1,1]、[1,1,1] 三种。最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。每次操作后都
需要求出,当前的魔咒串 S 共有多少种生成魔咒。

Algorithm Design

后缀自动机。每次加入一个新的节点的时候统计。

Code

#include <cstdio>
#include <map>
const int maxn = 100010;
int n, m, tot = 1, last = 1;
#define ll long long
ll ans;
int len[maxn << 1], fa[maxn << 1];
std::map<int, int> ch[maxn << 1];
int calc(int x) { return len[x] - len[fa[x]]; }
void insert(int x) {
  int p = last, np = last = ++tot;
  len[np] = len[p] + 1;
  while (p && !ch[p][x])
    ch[p][x] = np, p = fa[p];
  if (!p)
    fa[np] = 1, ans += calc(np);
  else {
    int q = ch[p][x];
    if (len[p] + 1 == len[q])
      fa[np] = q, ans += calc(np);
    else {
      int nq = ++tot;
      len[nq] = len[p] + 1;
      ch[nq] = ch[q];
      fa[nq] = fa[q];
      ans += calc(nq) - calc(q);
      fa[q] = fa[np] = nq;
      ans += calc(np) + calc(q);
      while (p && ch[p][x] == q)
        ch[p][x] = nq, p = fa[p];
    }
  }
}
int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    int x;
    scanf("%d", &x);
    insert(x);
    printf("%lld\n", ans);
  }
}

posted on 2017-03-14 21:45  蒟蒻konjac  阅读(148)  评论(0编辑  收藏  举报