题解「CF204E Little Elephant and Strings」
后缀数组+ST表+分块。
合法的子串必须满足至少是k个串的字串。这个要求让我们自然而然想到一道相似的题 P5546 [POI2000]公共串 ,这道题用后缀数组很容易想到解法。
于是开始后缀数组乱搞。
先将所有字符串用分隔符隔开连接成一个串。注意,这里的分隔符必须两两不同,否则求出的 \(height\) 数组将会不正确。
在所得串上跑一遍后缀排序,得到 \(height\) 数组。
然后我们使用双指针在 \(height\) 数组上来枚举合法的区间,左右端点的扩展类似于莫队,当区间内出现了 \(K\) 个原串的字串时,此区间合法。
用ST表求出区间内 \(height\) 的最小值 \(t\),即为区间内所有后缀的最长公共前缀,此时区间内所有后缀的长度为 \(t\) 的前缀均为合法字串。
不难发现一个性质:若后缀的长度为 \(t\) 的前缀合法,则该后缀长度为 \(t' \leq t\) 的前缀均合法。也就是说只需要记录每个后缀最长的合法前缀的长度即可。
需要支持区间对一个值取max,并单点查询。
因为 懒得想其他方法 分块好写,所以这里就直接上分块了。
\(\text{Code}:\)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define maxn 200005
#define R register
#define INF 0x3f3f3f3f
using namespace std;
typedef long long lxl;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
char s[maxn];
int N,M,K,a[maxn],n;
lxl ans[maxn];
int SA[maxn],rnk[maxn],tax[maxn],tp[maxn],ht[maxn],belong[maxn];
inline void Qsort()
{
for(int i=0;i<=M;++i) tax[i]=0;
for(int i=1;i<=N;++i) ++tax[rnk[i]];
for(int i=1;i<=M;++i) tax[i]+=tax[i-1];
for(int i=N;i>=1;--i) SA[tax[rnk[tp[i]]]--]=tp[i];
}
inline void SuffixSort()
{
M=30+n;
for(int i=1;i<=N;++i)
rnk[i]=a[i],tp[i]=i;
Qsort();
for(int w=1,p=0;p<N;M=p,w<<=1)
{
p=0;
for(int i=1;i<=w;++i) tp[++p]=N-w+i;
for(int i=1;i<=N;++i) if(SA[i]>w) tp[++p]=SA[i]-w;
Qsort();
swap(tp,rnk);
rnk[SA[1]]=p=1;
for(int i=2;i<=N;++i)
rnk[SA[i]]=(tp[SA[i]]==tp[SA[i-1]]&&tp[SA[i]+w]==tp[SA[i-1]+w]) ? p : ++p;
}
for(int i=1,k=0;i<=N;++i)
{
if(k) --k;
while(a[i+k]==a[SA[rnk[i]-1]+k]) ++k;
ht[rnk[i]]=k;
}
}
struct ST_Table
{
int d[maxn][30],lg[maxn];
inline void init()
{
lg[0]=-1;
for(int i=1;i<=N;++i) d[i][0]=ht[i],lg[i]=lg[i>>1]+1;
for(int j=1;j<=25;++j)
for(int i=1;i+(1<<(j-1))<=N;++i)
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
inline int query(int l,int r)
{
int k=lg[r-l+1];
return min(d[l][k],d[r-(1<<k)+1][k]);
}
}st;
struct BIG_Block// 大分块 (雾)
{
#define BN 400
lxl a[maxn],tag[maxn/BN];
inline int pos(int x) {return (x-1)/BN+1;}
inline void Get_Max(int l,int r,lxl d)
{
int bl=pos(l),br=pos(r);
for(int i=bl+1;i<br;++i)
tag[i]=max(tag[i],d);
if(bl==br)
for(int i=l;i<=r;++i)
a[i]=max(a[i],d);
else
{
for(int i=l;i<=bl*BN;++i)
a[i]=max(a[i],d);
for(int i=(br-1)*BN+1;i<=r;++i)
a[i]=max(a[i],d);
}
}
inline void Get_Val(int x)
{
a[x]=max(max(a[x],tag[pos(x)]),min(a[x-1],1ll*ht[x]));
}
}_9baka;
int cnt[maxn],flag;
inline void Add(int i)
{
if(!belong[i]) return;
if(!cnt[belong[i]]) ++flag;
++cnt[belong[i]];
}
inline void Del(int i)
{
if(!belong[i]) return;
--cnt[belong[i]];
if(!cnt[belong[i]]) --flag;
}
int main()
{
// freopen("CF204E.in","r",stdin);
n=read(),K=read();
if(K==1)
{
for(int i=1;i<=n;++i)
{
scanf(" %s",s+1);
int len=strlen(s+1);
printf("%lld ",1ll*(1+len)*len>>1);
}
return 0;
}
for(int i=1;i<=n;++i)
{
scanf(" %s",s+1);
int len=strlen(s+1);
for(int j=1;j<=len;++j)
a[++N]=s[j]-'a'+1,belong[N]=i;
a[++N]=26+i;
}
SuffixSort();
st.init();
for(int i=1,j=0;i<=N;++i)
{
while(j<=N&&flag<K) Add(SA[++j])
// 为了使 t 尽量大,这里只枚举出最短的合法区间
if(flag<K) break;
// 如果右端点枚举到边界都不合法,则左端点再缩小还是不合法,直接break
int t=st.query(i+1,j);
_9baka.Get_Max(i,j,t);
Del(SA[i]);
}
for(int i=2;i<=N;++i)
_9baka.Get_Val(i);
//若前后两后缀有公共前缀,则可能前面的后缀更新后面的后缀的答案
for(int i=1;i<=N;++i)
ans[belong[SA[i]]]+=_9baka.a[i];
// 最长合法前缀的长度即为合法前缀的个数
for(int i=1;i<=n;++i)
printf("%lld ",ans[i]);
return 0;
}