SP8222 NSUBSTR - Substrings

知识点: SAM

原题面 Luogu


分析题意

SAM 板子背诵检查。
按拓扑序求出每个状态出现次数,仅更新 \(F(\operatorname{len}(i))\)
长的包含短的,显然有 \(F(i) = \max\limits_{j=i+1}^{n}\{F(j)\}\)


代码实现

//知识点:SAM
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define ll long long
const int kMaxn = 5e5 + 10;
const int kMaxm = 26;
//=============================================================
int n, last = 1, node_num = 1, f[kMaxn];
int ch[kMaxn << 1][kMaxm], link[kMaxn << 1], len[kMaxn << 1], size[kMaxn << 1];
int edge_num, v[kMaxn << 1], ne[kMaxn << 1], head[kMaxn << 1];
char S[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;
}
void GetMax(int &fir, int sec) {
  if (sec > fir) fir = sec;
}
void GetMin(int &fir, int sec) {
  if (sec < fir) fir = sec;
}
void AddEdge(int u_, int v_) {
  v[++ edge_num] = v_, ne[edge_num] = head[u_], head[u_] = edge_num;
}
void Insert(int c_) {
  int p = last, now = last = ++ node_num;
  size[now] = 1, len[now] = len[p] + 1;
  for (; p && ! ch[p][c_]; p = link[p]) ch[p][c_] = now;
  if (! p) {link[now] = 1; return ;}  
  int q = ch[p][c_];
  if (len[q] == len[p] + 1) {link[now] = q; return ;}
  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;
  for (; p && ch[p][c_] == q; p = link[p]) ch[p][c_] = newq;
}
void Dfs(int u_) { 
  for (int i = head[u_]; i; i = ne[i]) {
    Dfs(v[i]);
    size[u_] += size[v[i]];
  }
  GetMax(f[len[u_]], size[u_]);
}
//=============================================================
int main() { 
  scanf("%s", S + 1);
  n = strlen(S + 1);
  for (int i = 1; i <= n; ++ i) Insert(S[i] - 'a');
  for (int i = 2; i <= node_num; ++ i) AddEdge(link[i], i);
  Dfs(1);
  for (int i = n; i >= 1; -- i) GetMax(f[i], f[i + 1]);
  for (int i = 1; i <= n; ++ i) printf("%d\n", f[i]);
  return 0; 
}
posted @ 2020-08-17 08:16  Luckyblock  阅读(125)  评论(2)    收藏  举报