广义后缀树+set
子串问题就考虑后缀数据结构。考虑一棵后缀树,一个树上的串合法,当且仅当这个串在至少k个串的后缀树链的并上。那就用SAM做出后缀树,用set维护一个点的链并,即哪些串经过了这个点。然后枚举每一个串跑SAM,对于一个位置i,暴力跳fail直到链并不小于k为止。
广义SAM和单串SAM的差别就是刚开始extend的时候要判断是否有一个状态已经包含了当前要增加的状态并且判断这个状态的len和当前要增加的状态的len的关系。
听说set的合并要用a.swap(b),这样复杂度是O(1)的。直接swap我也不知道复杂度如何。
#include<set>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define A 28
#define H 20
using namespace std;
namespace runzhe2000
{
typedef long long ll;
vector<int> node[N];
char s[N], *str[N];
int ecnt, n, k, last[N<<1], good[N<<1];
struct edge{int next, to;}e[N<<1];
void addedge(int a, int b)
{
e[++ecnt] = (edge){last[a], b};
last[a] = ecnt;
}
struct SAM
{
SAM *fail, *next[A];
int len;
set<int> S;
}mem[N<<1], *tot, *null, *root;
SAM* newSAM(int v = 0)
{
*++tot = *null;
if(v)tot->S.insert(v), node[v].push_back(tot - mem);
return tot;
}
void init()
{
null = tot = mem; null->fail = null; null->len = 0;
for(int i = 0; i < A; i++) null->next[i] = null;
root = newSAM();
}
SAM* extend(SAM *p, int v, int id)
{
if(p->next[v] != null)
{
SAM *q = p->next[v];
if(p->len + 1 == q->len) {q->S.insert(id); return q;}
else
{
SAM *nq = newSAM(id);
nq->fail = q->fail; nq->len = p->len + 1;
for(int i = 0; i < A; i++) nq->next[i] = q->next[i];
q->fail = nq;
for(; p->next[v] == q && p != null; p = p->fail) p->next[v] = nq;
return nq;
}
}
else
{
SAM *np = newSAM(id); np->len = p->len + 1;
for(; p->next[v] == null && p != null; p = p->fail) p->next[v] = np;
if(p == null) np->fail = root;
else
{
SAM *q = p->next[v];
if(p->len + 1 == q->len) np->fail = q;
else
{
SAM *nq = newSAM(id);
nq->fail = q->fail; nq->len = p->len + 1;
for(int i = 0; i < A; i++) nq->next[i] = q->next[i];
np->fail = q->fail = nq;
for(; p->next[v] == q && p != null; p = p->fail) p->next[v] = nq;
}
}
return np;
}
}
void build_tree()
{
for(SAM *i = tot; i != mem; i--)
addedge(i->fail-mem, i-mem);
}
void merge_set(set<int> &a, set<int> &b)
{
if(a.size() < b.size()) a.swap(b); // 这样换set是O(1)的
for(; !b.empty(); ) a.insert(*b.begin()), b.erase(b.begin());
}
void dfs(int x)
{
for(int i = last[x]; i; i = e[i].next)
{
int y = e[i].to;
dfs(y); merge_set(mem[x].S, mem[y].S);
}
if((int)mem[x].S.size() >= k) good[x] = 1;
}
void main()
{
scanf("%d%d",&n,&k);
if(n < k) {for(int i = 1; i <= n; i++)printf("0 "); return;}
init();
for(int i = 1; i <= n; i++)
{
scanf("%s",s);
str[i] = new char[strlen(s) + 5]();
memcpy(str[i], s, sizeof(char)*(strlen(s)+5));
SAM *last = root;
for(int j = 0, jj = strlen(s); j < jj; j++)
last = extend(last, s[j] - 'a', i);
}
build_tree();
dfs(root - mem);
for(int i = 1; i <= n; i++)
{
SAM *p = root; ll ans = 0;
for(int j = 0, jj = strlen(str[i]); j < jj; j++)
{
p = p->next[str[i][j] - 'a'];
for(; !good[p-mem]; p = p->fail);
ans += p->len;
}
printf("%lld ",ans);
}
}
}
int main()
{
runzhe2000::main();
}