解法二【广义后缀自动机】见【这里】。
首先把串拼在一起求后缀数组,对于每一个字符串
si
从前往后考虑,找到这一位
j
所能延伸的最大长度
对于答案
x
,我们可以在sa上二分找到前后最远的一段,这一段包含现在考虑的位置
最后的复杂度是
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define LL long long
const int maxn=800010,oo=0x3f3f3f3f;
char s1[maxn];
int s[maxn],sa[maxn],rank[maxn],height[maxn],cnt[maxn],f[maxn],
bel[maxn],start[maxn],len[maxn],left[maxn],mn[maxn][25],log[maxn],
n,k,l;
LL ans[maxn];
int qry(int l,int r)
{
if (l>r) return oo;
int k=log[r-l+1];
return min(mn[l][k],mn[r-(1<<k)+1][k]);
}
int check(int p,int x)
{
int L=2,R=p+1,u,v;
while (L<R)
{
int mid=(L+R)/2;
if (qry(mid,p)<x) L=mid+1;
else R=mid;
}
u=L-1;
L=p,R=l;
while (L<R)
{
int mid=(L+R+1)/2;
if (qry(p+1,mid)<x) R=mid-1;
else L=mid;
}
v=L;
return left[v]>=u;
}
int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
int m,p,now=0;
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
{
start[i]=l+1;
scanf("%s",s1+1);
len[i]=strlen(s1+1);
l+=len[i];
for (int j=start[i];j<=l;j++)
{
bel[j]=i;
s[j]=s1[j-start[i]+1];
}
s[++l]='z'+i;
}
m='z'+n-'a'+1;
for (int i=1;i<=l;i++)
cnt[rank[i]=s[i]-'a'+1]++;
for (int i=2;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=l;i;i--) sa[cnt[rank[i]]--]=i;
for (int x=1;;x<<=1)
{
p=0;
for (int i=l-x+1;i<=l;i++) f[++p]=i;
for (int i=1;i<=l;i++)
if (sa[i]>x) f[++p]=sa[i]-x;
for (int i=1;i<=m;i++) cnt[i]=0;
for (int i=1;i<=l;i++) cnt[rank[f[i]]]++;
for (int i=2;i<=m;i++) cnt[i]+=cnt[i-1];
for (int i=l;i;i--) sa[cnt[rank[f[i]]]--]=f[i];
for (int i=1;i<=l;i++) f[i]=rank[i];
rank[sa[1]]=1;
for (int i=2;i<=l;i++)
if (f[sa[i]]==f[sa[i-1]]&&f[sa[i]+x]==f[sa[i-1]+x])
rank[sa[i]]=rank[sa[i-1]];
else rank[sa[i]]=rank[sa[i-1]]+1;
m=rank[sa[l]];
if (m>=l) break;
}
for (int i=1;i<=l;i++)
{
if ((height[rank[i]]=height[rank[i-1]])) height[rank[i]]--;
while (s[i+height[rank[i]]]==s[sa[rank[i]-1]+height[rank[i]]]) height[rank[i]]++;
}
for (int i=1;i<=l;i++) mn[i][0]=height[i];
for (int i=1;(1<<i)<=l;i++) log[1<<i]=i;
for (int i=3;i<=l;i++)
if (!log[i]) log[i]=log[i-1];
for (int j=1;j<=log[l];j++)
for (int i=1;i+(1<<j)-1<=l;i++)
mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);
for (int i=1;i<=n;i++) cnt[i]=0;
for (int i=1;i<=l;i++)
{
p=i;
cnt[bel[sa[i]]]++;
if (bel[sa[i]]&&cnt[bel[sa[i]]]==1) now++;
if (now>=k) break;
}
for (int i=1;i<p;i++) left[i]=-oo;
cnt[bel[sa[p]]]--;
now--;
for (int i=p,j=1;i<=l;i++)
{
cnt[bel[sa[i]]]++;
if (bel[sa[i]]&&cnt[bel[sa[i]]]==1) now++;
while (now>k||!bel[sa[j]]||cnt[bel[sa[j]]]>1)
{
cnt[bel[sa[j]]]--;
if (bel[sa[j]]&&!cnt[bel[sa[j]]]) now--;
j++;
}
left[i]=j;
}
for (int i=1;i<=n;i++)
{
now=0;
for (int j=1;j<=len[i];j++,now--)
{
while (j+now<=len[i]&&check(rank[start[i]+j-1],now+1)) now++;
ans[i]+=now;
}
}
for (int i=1;i<=n;i++) printf("%lld ",ans[i]);
}