[bzoj3277==bzoj3473]出現k次子串計數——廣義后綴自動機+STL


Brief Description

給定n個字符串,對於每個字符串,您需要求出在所有字符串中出現次數大於等於k次的子串個數。

Algorithm Design

先建立一個廣義后綴自動機,什么是廣義后綴自動機?就是所有主串一起建立的一個后綴自動機。
廣義后綴自動機的建立很簡單,對於每個串,該怎么增量建立自動機就怎么建立,只不過為每個節點維護一個set保存這個節點的狀態在那些字符串中出現過。當一個串增量構建完畢后,將后綴自動機的last指針指向后綴自動機的根即可進行下一發字符串的增量構建,這樣就建出來了一發廣義后綴自動機。
考慮一個節點,如果他在x個字符串中出現過,那么他的fa指針所指向的節點所代表的狀態出現過的次數一定不小於他。
並且我們已經為每個節點維護了一個set來記錄在那些字符串中出現過,那么我們只需要自下向上合並set集合即可,在這之前需要整理出parent樹的具體形態,然后一遍dfs,逆序處理set的啟發式合並即可。
統計答案只需把每個字符串在自動機上跑,跑到一個節點發現出現次數<K就往fa指針那里跳,直到符合條件。這時候貢獻的答案就是當前節點的len屬性的值了.

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <set>
#include <string>
const int maxn = 200010;
using std::set;
using std::string;
#define ll long long
set<int> d[maxn];
set<int>::iterator it;
int n, K, tot = 1, head[maxn], sum[maxn];
struct edge {
  int to, next;
} e[maxn * 6];
string str[maxn];
struct Suffix_Automaton {
  int trans[maxn][26], len[maxn], sz;
  int fa[maxn], last, root;
  void init() {
    tot = 0;
    last = root = ++sz;
  }
  void add(int c, int id) {
    int p = last, np = last = ++sz;
    len[np] = len[p] + 1;
    d[np].insert(id);
    while (p && !trans[p][c])
      trans[p][c] = np, p = fa[p];
    if (!p)
      fa[np] = root;
    else {
      int q = trans[p][c];
      if (len[q] == len[p] + 1)
        fa[np] = q;
      else {
        int nq = ++sz;
        len[nq] = len[p] + 1;
        fa[nq] = fa[q];
        for (int i = 0; i < 26; i++)
          trans[nq][i] = trans[q][i];
        fa[q] = fa[np] = nq;
        while (trans[p][c] == q)
          trans[p][c] = nq, p = fa[p];
      }
    }
  }
  void print() {
    for (int i = 1; i <= sz; i++) {
      std::cout << fa[i] << ' ';
    }
    std::cout << std::endl;
    for (int i = 1; i <= sz; i++)
      printf("%d ", sum[i]);
    printf("\n");
  }
} sam;
void dfs(int x) {
  for (int i = head[x]; i; i = e[i].next) {
    int v = e[i].to;
    dfs(v);
    for (it = d[v].begin(); it != d[v].end(); it++)
      d[x].insert(*it);
  }
  sum[x] = d[x].size();
}
void add_edge(int from, int to) {
  e[++tot].to = to;
  e[tot].next = head[from];
  head[from] = tot;
}
int main() {
#ifndef ONLINE_JUDGE
  freopen("input", "r", stdin);
#endif
  scanf("%d %d", &n, &K);
  sam.init();
  for (int i = 1; i <= n; i++) {
    std::cin >> str[i];
    int len = str[i].length();
    for (int j = 0; j < len; j++)
      sam.add(str[i][j] - 'a', i);
    sam.last = sam.root;
  }
  for (int i = 1; i <= sam.sz; i++)
    if (sam.fa[i])
      add_edge(sam.fa[i], i);
  dfs(sam.root);
  // sam.print();
  if (K > n) {
    for (int i = 1; i <= n; i++)
      printf("0 ");
    return 0;
  }
  for (int i = 1; i <= n; i++) {
    ll ans = 0;
    int now = sam.root, len = str[i].length();
    for (int j = 0; j < len; j++) {
      now = sam.trans[now][str[i][j] - 'a'];
      while (sum[now] < K)
        now = sam.fa[now];
      ans += sam.len[now];
    }
    printf("%lld ", ans);
  }
  return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM