题意
给出n个字符串,问每个字符串中有多少子串是这所有的n个字符串中至少k个的子串。
分析
广义后缀自动机模板题。对这n个串建广义后缀自动机,对于每个状态维护两个值cou[u]和lcu[u]分别代表拥有这个状态的子串的数量和上一次更新到这个状态的子串的数量。然后设f[u]为状态u到祖先的所有结点有多少子串出现在至少k个字符串中。然后再跑一边每个子串就可以了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream> using namespace std;
const int maxn=1e5+;
typedef long long LL;
string S[maxn];
char s[maxn];
struct state{
int link,len;
int next[];
}st[*maxn];
int n,k,last,cur,sz;
int c[*maxn];
int cou[*maxn],lcu[*maxn];//不同子串数量,上一次出现的子串
LL f[maxn]; void init(){
sz=;
cur=last=;
st[].link=-;
st[].len=;
}
void build_sam(int c){
cur=sz++;
st[cur].len=st[last].len+;
int p;
for(p=last;p!=-&&st[p].next[c]==;p=st[p].link)
st[p].next[c]=cur;
if(p==-)
st[cur].link=;
else{
int q=st[p].next[c];
if(st[p].len+==st[q].len)
st[cur].link=q;
else{
int clone=sz++;
st[clone].len=st[p].len+;
st[clone].link=st[q].link;
for(int i=;i<;i++)
st[clone].next[i]=st[q].next[i];
for(;p!=-&&st[p].next[c]==q;p=st[p].link)
st[p].next[c]=clone;
st[cur].link=st[q].link=clone;
}
}
last=cur;
}
int cmp(int a,int b){
return st[a].len<st[b].len;
} int main(){
scanf("%d%d",&n,&k);
init();
for(int i=;i<=n;i++){
scanf("%s",s);
S[i]=(string)s;
int len=strlen(s);
for(int i=;i<len;i++)
build_sam(s[i]-'a');
last=;
}
for(int i=;i<=n;i++){
int u=;
for(int j=;j<S[i].length();j++){
int c=S[i][j]-'a';
u=st[u].next[c];
for(int p=u;p!=-&&lcu[p]!=i;p=st[p].link)
lcu[p]=i,cou[p]++;
}
}
for(int i=;i<sz;i++)
c[i]=i;
sort(c,c+sz,cmp);
for(int i=;i<sz;i++){
int u=c[i];
if(st[u].link!=-){
f[u]+=f[st[u].link];
if(cou[u]>=k){
f[u]+=st[u].len-st[st[u].link].len;
}
}
}
for(int i=;i<=n;i++){
int u=;
LL res=;
for(int j=;j<S[i].length();j++){
int c=S[i][j]-'a';
u=st[u].next[c];
res+=f[u];
}
printf("%lld\n",res);
} return ;
}