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

23 篇文章 1 订阅

题目大意

由于学校需要一些材料,校长需要在文章中检索一些信息。校长一共给了小可可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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值