3277: 串
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 575 Solved: 236
[ Submit][ Status][ Discuss]
Description
字符串是oi界常考的问题。现在给定你n个字符串,询问每个字符串有多少子串(不包括空串)是所有n个字符串中至少k个字符串的子串(注意包括本身)。
Input
第一行两个整数n,k。
接下来n行每行一个字符串。
接下来n行每行一个字符串。
Output
输出一行n个整数,第i个整数表示第i个字符串的答案。
Sample Input
3 1
abc
a
ab
Sample Output
6 1 3
HINT
对于100%的数据,n,k,l<=100000
Source
【分析】
后缀自动机
建立广义后缀自动机,统计每个状态在哪些串中出现过(parent树从下往上更新),如果某个状态出现在了m(或者>m)个串中,那么这个点对应的串都要统计到答案里...否则不用管它。
woc这做法巧妙死了。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
#define ll long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int mxn=200005;
char s[mxn];
ll c[mxn],ans[mxn];
int n,m,len;
int p,q,np,nq,tot,cnt,root;
int step[mxn],pre[mxn],son[mxn][30];
int head[mxn],size[mxn],type[mxn],kind[mxn];
struct edge {int to,next;} f[mxn];
inline void add(int u,int v)
{
f[++cnt].to=v,f[cnt].next=head[u],head[u]=cnt;
}
inline void sam(int t,int c)
{
p=np;
step[np=(++tot)]=step[p]+1;
kind[np]=t;
while(p && !son[p][c])
son[p][c]=np,p=pre[p];
if(!p) pre[np]=1;
else
{
q=son[p][c];
if(step[q]==step[p]+1)
pre[np]=q;
else
{
step[nq=(++tot)]=step[p]+1;
memcpy(son[nq],son[q],sizeof son[q]);
size[nq]=size[q],type[nq]=type[q];
pre[nq]=pre[q];
pre[np]=pre[q]=nq;
while(p && son[p][c]==q)
son[p][c]=nq,p=pre[p];
}
}
for(p=np;p && type[p]!=t;p=pre[p])
size[p]++,type[p]=t;
}
inline void dfs(int u)
{
c[u]+=c[pre[u]];
ans[kind[u]]+=c[u];
for(int i=head[u];i;i=f[i].next)
dfs(f[i].to);
}
int main()
{
scanf("%d%d",&n,&m);
tot=1;
fo(i,1,n)
{
scanf("%s",s+1);
len=strlen(s+1);
root=np=1;
fo(j,1,len)
sam(i,s[j]-'a'+1);
}
fo(i,2,tot)
{
add(pre[i],i);
if(size[i]>=m) c[i]=step[i]-step[pre[i]];
else c[i]=0;
}
dfs(1);
fo(i,1,n) printf("%lld ",ans[i]);
return 0;
}