您的位置:首页 > 其它

【jzoj3870】【单词检索】【后缀数组】

2017-01-16 11:42 351 查看

题目大意

由于学校需要一些材料,校长需要在文章中检索一些信息。校长一共给了小可可N篇文章,每篇文章为一个字符串。现在,校长需要他找到这样的单词,它至少在这N篇文章中的M篇文章里出现过,且单词长度为L。可是,工作量十分庞大,但校长又急需小可可完成这项任务。现在他向你求助,需要你编写程序完成这项艰巨的任务。

解题思路

题解是hash。我使用的是sa。考虑把所有的串并在一起,中间用没出现过的符号分隔。按height>=l分组,看每组的串是否来自>=m个串,统计答案即可。

code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LF double
#define LL long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
using namespace std;
int const maxn=2000,maxl=1000,inf=1e9;
int n,m,l,N,M,cnt[maxn*maxl+10],A[2][maxn*maxl+10],sa[maxn*maxl+10],
h[maxn*maxl+10],num[maxn*maxl+10],s[maxn*maxl+10];
int *rank=A[0],*ord=A[1];
char S[maxl+10];
void sort(){
fo(i,0,M)cnt[i]=0;
fo(i,1,N)cnt[rank[i]]++;
fo(i,1,M)cnt[i]+=cnt[i-1];
fd(i,N,1)sa[cnt[rank[ord[i]]]--]=ord[i];
}
bool diff(int x,int y,int w){
return (ord[x]!=ord[y])||(ord[x+w]!=ord[y+w]);
}
int main(){
freopen("d.in","r",stdin);
freopen("d.out","w",stdout);
scanf("%d%d%d",&n,&m,&l);N=1;int len;
fo(cas,1,n){
scanf("%s",S);len=strlen(S);
fo(i,N,N+len-1)num[i]=cas,s[i]=S[i-N];
s[N+len]=256+cas;N+=len+1;
}N--;M=256+n;
fo(i,1,N)rank[i]=s[i],ord[i]=i;
sort();
for(int w=1,p=0;p<N;w=w<<1){
M=p,p=0;fo(i,N-w+1,N)ord[++p]=i;
fo(i,1,N)if(sa[i]>w)ord[++p]=sa[i]-w;
sort();swap(rank,ord);rank[sa[1]]=p=1;
fo(i,2,N)rank[sa[i]]=(p+=diff(sa[i-1],sa[i],w));
}
for(int i=1,p=0;i<=N;h[rank[i]]=p,i++)
for(p-=(p!=0);s[i+p]==s[sa[rank[i]-1]+p];p++);
int ans=0;fo(i,1,n)cnt[i]=0;
for(int i=1,j,tmp;i<=N;){
j=i;tmp=0;while((j<N)&&(h[j+1]>=l))j++;
fo(k,i,j){
if(!cnt[num[sa[k]]])tmp++;
cnt[num[sa[k]]]=1;
}
if(tmp>=m)
ans++;
fo(k,i,j)cnt[num[sa[k]]]=0;
i=j+1;
}
printf("%d",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: