Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
3
a
aa
aaa
a
aa
aaa
Sample Output
6
3
1
题解:
3
1
先将所有字符串拼起来,然后求一遍后缀数组,找到第以i个串开头的后缀的位置,求出向左走保证height[x]>=len[i]的最小的x,以及向右走height[y]>=len[i]的最大的y,那么答案就是y-x+2;
向左扩展或向右扩展在扩展的过程中,min(height[x])肯定单调不增,所以可以二分答案,rmq预处理区间最小值,时间复杂度O(nlogn)
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1000000+210;
char A[maxn];
int sa[maxn],rk[maxn],tmp[maxn],c[maxn];
int n;
int minn[maxn][22];
int sum[maxn];
int len[maxn];
int height[maxn];
int lg[maxn];
int tot=0;
char B[maxn];
inline void get_sa(){
int *rnk=rk,*tp=tmp;
n=strlen(A+1);
int m=300;
for(int i=1;i<=n;i++)
tp[i]=A[i],rnk[i]=A[i];
for(int i=0;i<=m;i++)
c[i]=0;
for(int i=1;i<=n;i++)
c[tp[i]]++;
for(int i=1;i<=m;i++)
c[i]+=c[i-1];
for(int i=n;i>=1;i--)
sa[c[tp[i]]--]=i;
for(int j=1;j<=n;j<<=1){
int p=0;
for(int i=n-j+1;i<=n;i++)
tp[++p]=i;
for(int i=1;i<=n;i++)
if(sa[i]>j)
tp[++p]=sa[i]-j;
for(int i=0;i<=m;i++)
c[i]=0;
for(int i=1;i<=n;i++)
c[rnk[tp[i]]]++;
for(int i=1;i<=m;i++)
c[i]+=c[i-1];
for(int i=n;i>=1;i--)
sa[c[rnk[tp[i]]]--]=tp[i];
swap(rnk,tp);
rnk[sa[1]]=1;
p=1;
for(int i=2;i<=n;i++){
int O1=sa[i]+j>n?-1:tp[sa[i]+j];
int O2=sa[i-1]+j>n?-1:tp[sa[i-1]+j];
rnk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&O1==O2)?p:++p;
}
m=p;
if(m>=n)
break;
}
}
inline void get_height(){
for(int i=1;i<=tot;i++)
rk[sa[i]]=i;
int h=0;
for(int i=1;i<=tot;i++){
--h=h<0?0:h;
int u=sa[rk[i]-1];
while(A[u+h]==A[i+h])
h++;
height[rk[i]]=h;
}
}
inline void rmq_pre(){
for(int i=1;i<=tot;i++){
int u=1,cnt=0;
while(u<=i)
u<<=1,cnt++;
lg[i]=cnt-1;
}
for(int i=1;i<=tot;i++)
minn[i][0]=height[i];
for(int j=1;j<=30;j++)
for(int i=1;i<=tot;i++){
if(i+(1<<j)-1>tot)
break;
minn[i][j]=min(minn[i][j-1],minn[i+(1<<(j-1))][j-1]);
}
}
inline int get_min(int l,int r){
if(l>r)
return 0x3f3f3f3f;
int u=lg[r-l+1];
return min(minn[l][u],minn[r-(1<<u)+1][u]);
}
inline int get_l(int x,int length){
int l=1,r=x;
while(l+1<r){
int mid=(l+r)>>1;
int u=get_min(mid+1,x);
if(u>=length)
r=mid;
else l=mid;
}
if(get_min(l+1,x)>=length)
return l;
return r;
}
inline int get_r(int x,int length){
int l=x,r=tot;
while(l+1<r){
int mid=(l+r)>>1;
int u=get_min(x+1,mid);
if(u>=length)
l=mid;
else r=mid;
}
if(get_min(x+1,r)>=length)
return r;
return l;
}
inline void work(int i){
int t=rk[sum[i-1]+1];
int l=get_l(t,len[i]),r=get_r(t,len[i]);
//printf("%d %d\n",l,r);
printf("%d\n",r-l+1);
}
int main(){
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",B+1);
len[i]=strlen(B+1);
for(int j=1;j<=len[i];j++)
A[++tot]=B[j];
if(i!=n)
A[++tot]='z'+1;
sum[i]=sum[i-1]+len[i]+(i!=n);
}
get_sa();
get_height();
rmq_pre();
for(int i=1;i<=n;i++)
work(i);
//for(int i=1;i<=tot;i++)
// printf("%d ",sa[i]);
return 0;
}