【JZOJ 3870】单词检索

Description

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

Solution

这题显然可以字符串哈希,
用两个哈希,枚举每一个串,再枚举开头,先看一下这个子串在当前的整个串中是不是第一次出现,再把它的哈希值加1,相应的,判断一下这个哈希值出现的次数是否等于M,

为了保险,建议使用双哈希,

复杂度: O(nm)

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long LL;
const int N=2200,mo=1e9+7,mo1=998244353;
const int moh=1564437;
int n,m,m1,ans;
int a[N][N/2];
int nx[N][N/2];
struct qqww{int a,b;};
int z[moh][3];
int Z[moh][2],z1[moh][2];
// map<int,qqww>z;
// map<int,int>z1,Z;
bool H1(int q,int w)
{
    int i=q%moh;
    while(Z[i][0]&&Z[i][0]!=q)i=(i+1)%moh;
    Z[i][0]=q;
    if(Z[i][1]<w){Z[i][1]=w;return 1;}
    return 0;
}
int H(int q)
{
    int i=q%moh;
    while(z[i][0]&&z[i][0]!=q)i=(i+1)%moh;
    return i;
}
int H2(int q)
{
    int i=q%moh;
    while(z1[i][0]&&z1[i][0]!=q)i=(i+1)%moh;
    return i;
}
int main()
{
    scanf("%d%d%d",&n,&m,&m1);
    fo(i,1,n)
    {
        char ch=' ';
        for(;ch>'z'||ch<'a';ch=getchar());
        for(;ch>='a'&&ch<='z';ch=getchar())a[i][++a[i][0]]=ch-96;
    }
    ans=0;
    LL q,w,q1,w1;
    LL qn=576923081,wn=729486258;
    int t;
    fo(I,1,n)
    {
        q1=w1=1;q=w=0;
        fo(i,1,m1-1)q=(q+(a[I][i]-1)*q1)%mo,w=(w+(a[I][i]-1)*w1)%mo1,q1=q1*26%mo,w1=w1*26%mo1;
        fo(i,m1,a[I][0])
        {
            q=(q+(a[I][i]-1)*q1)%mo,w=(w+(a[I][i]-1)*w1)%mo1;
            if(H1(q,I))
            {
                t=H(q);
                if(z[t][2]==w||z[t][2]==0)
                {
                    z[t][2]=w;
                    if((++z[t][1])==m)ans++;
                }else
                {
                    t=H2(w);
                    if((++z1[t][2])==m)ans++;
                }
            }
            q=(q-a[I][i-m1+1]+1)*qn%mo;
            w=(w-a[I][i-m1+1]+1)*wn%mo1;
        }
    }
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值