【题解】CF204E Little Elephant and Strings
CF204E Little Elephant and Strings
\(\text{Solution:}\)
第一眼看上去就已经很广义 SAM 了。
考虑我们对每一个子串维护它在多少串中出现过。对每一个串的尾节点进行染色,问题就变成在 parent 树上求子树颜色数的问题。可以用 dfs序 转化成序列问题。
那接下来如何统计?对每个串,我们考虑依次匹配其前缀以获取其前缀对应匹配到的最长后缀来获得所有子串。
首先我们定位到这个前缀节点的时候,其出现的子串数目是可能小于 \(k\) 的。这个时候就需要往上跳了,那暴力只有随机的时候复杂度才对……
考虑到,其父亲节点的出现数目必然是不降的,所以它具有某种单调性,考虑倍增出后缀树上每个节点的祖先,每次倍增跳即可做到一个 \(\log.\)
复杂度 \(O(\sum |S|\log \sum|S|)\)
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
#define int long long
int tr[N],n,k;
string s[N];
vector<int>nodepos[N];
inline int lowbit(int x){return x&(-x);}
inline void change(int x,int v,int M){while(x<=M)tr[x]+=v,x+=lowbit(x);}
inline int query(int x){
int res=0;
while(x)res+=tr[x],x-=lowbit(x);
return res;
}
namespace SAM{
int len[N],pa[N],ch[N][26],last=1,tot=1,v[N];
int siz[N],pos[N],cpos[N];
int dfn[N],rk[N],dfstime,dep[N],num[N],f[N][20];
vector<int>G[N],qr[N];
void insert(const int &c,const int &col){
int p=last;int np=++tot;last=tot;
len[np]=len[p]+1;v[np]=col;
for(;p&&!ch[p][c];p=pa[p])ch[p][c]=np;
if(!p)pa[np]=1;
else{
int q=ch[p][c];
if(len[q]==len[p]+1)pa[np]=q;
else{
int nq=++tot;
len[nq]=len[p]+1;
memcpy(ch[nq],ch[q],sizeof ch[q]);
pa[nq]=pa[q];pa[q]=pa[np]=nq;
for(;p&&ch[p][c]==q;p=pa[p])ch[p][c]=nq;
}
}
}
void dfs(int x){
siz[x]=1;
rk[dfn[x]=++dfstime]=x;
for(auto vv:G[x]){
f[vv][0]=x;dep[vv]=dep[x]+1;
for(int i=1;i<20;++i)f[vv][i]=f[f[vv][i-1]][i-1];
dfs(vv);siz[x]+=siz[vv];
}
}
void Build(){
for(int i=2;i<=tot;++i)G[pa[i]].push_back(i);
dfs(1);
}
void work(){
for(int i=1;i<=tot;++i){
//i->dfs
//rk[i]=node
int node=rk[i];
qr[i+siz[node]-1].push_back(node);
if(!cpos[v[node]])cpos[v[node]]=i;
pos[i]=cpos[v[node]];cpos[v[node]]=i;
}
for(int i=2;i<=tot;++i){
if(v[rk[i]]==0)continue;
change(i,1,tot);
if(pos[i]!=i)change(pos[i],-1,tot);
for(auto qv:qr[i]){
num[qv]=query(i)-query(dfn[qv]-1);
}
}
}
}
using namespace SAM;
int Ans[N];
void solve(int col,int node){
for(int i=19;~i;--i)if(f[node][i]&&num[f[node][i]]<k)node=f[node][i];
if(num[node]<k)node=f[node][0];
Ans[col]+=len[node];
}
inline bool cmp(int x,int y){return dfn[x]<dfn[y];}
signed main(){
scanf("%lld%lld",&n,&k);
for(int i=1;i<=n;++i){
last=1;
cin>>s[i];
int slen=s[i].size();
for(int j=0;j<slen;++j)insert(s[i][j]-'a',i);
}
for(int i=1;i<=n;++i){
int slen=s[i].size();
int now=1;
for(int j=0;j<slen;++j){
int v=s[i][j]-'a';
now=ch[now][v];
nodepos[i].push_back(now);
}
}
Build();
work();
for(int i=1;i<=n;++i){
int slen=s[i].size();
for(int j=0;j<slen;++j){
int pn=nodepos[i][j];
solve(i,pn);
}
}
for(int i=1;i<=n;++i)printf("%lld ",Ans[i]);
return 0;
}