单词检索题解

本文介绍了一种用于检索特定长度单词的高效算法,该算法利用三重哈希表来处理大量文本数据,旨在找出在多篇文章中至少出现指定次数的单词。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 单词检索

(search.pas/c/cpp)

【问题描述】

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

【输入格式】

第1行3个正整数N,M,L,表示文章的数目,单词至少出现在M篇文章中和每个单词的长度。
接下来N行,每行一个字符串,表示一篇文章。

【输出格式】

仅一行,表示满足检索条件的单词数。

【样例】

search.in search.out
3 2 2
noip
istudycpp
imacppstudent 5

【样例解释】

这5个单词分别为:st,tu,ud,pp,cp。

【数据范围】

对于20%的数据有1≤N,M≤10;
对于60%的数据有1≤N,M≤100;
对于100%的数据有1≤N,M≤2000,L≤1000。每篇文章长度不大于1000,均有小写字母组成。

三重哈希,对于一篇文章,先算出0-L-1位的hash值 xxx=pL1s0+pL2s1+...+p0sL1
对于1-L位的hash值yy,y=pL1s1+pL2s2+...+p0sL
可以用xx递推出y

#include<cstdio>
#include<cstring>
#include<vector>
#define ll long long
using namespace std;

const int p1=29,p2=37,p3=41;
const ll Mod1=10003,Mod2=1000007,Mod3=1000009; //这里用了三重hash,其实对拍出来有数据可以卡掉,Mod3应该取
                                                //大一点,如998244353

struct Node {
    int a,b,c;
};

int n,m,l;
vector<Node> a[Mod1+10],A[Mod1+10]; //比较懒的我打了vector实现hash表
char s[1010];
ll s1,s2,s3;

inline bool se(int x,int y,int z) {
    for(register unsigned int i=0;i<A[x].size();i++) {
        if(A[x][i].a==y && A[x][i].b==z) {
            return true;
        }
    }
    return false;
}

inline void inse(int x,int y,int z) {
    for(register unsigned int i=0;i<a[x].size();i++) {
        if(a[x][i].a==y && a[x][i].b==z) {
            a[x][i].c++;
            return;
        }
    }
    a[x].push_back((Node){y,z,1});
}

inline void Inse(int x,int y,int z) {
    for(register unsigned int i=0;i<A[x].size();i++) {
        if(A[x][i].a==y && A[x][i].b==z) {
            A[x][i].c++;
            return;
        }
    }
    A[x].push_back((Node){y,z,1});
}

int main()
{
    freopen("search.in","r",stdin);
    freopen("search.out","w",stdout);
    scanf("%d%d%d",&n,&m,&l);
    long long o1=1,o2=1,o3=1;
    for(int i=1;i<=l-1;i++) {
        o1=(o1*p1)%Mod1;
        o2=(o2*p2)%Mod2;
        o3=(o3*p3)%Mod3;
    }
    for(int j=1;j<=n;j++) {
        for(register int i=0;i<Mod1;i++) {
            A[i].clear();
        }
        scanf("%s",s);
        int len=strlen(s);
        if(len<l) continue;
        s1=s2=s3=0;
        for(int i=0;i<l;i++) {
            s1=(s1*p1+s[i])%Mod1;
            s2=(s2*p2+s[i])%Mod2;
            s3=(s3*p3+s[i])%Mod3;
        }
        if(!se(s1,s2,s3)) inse(s1,s2,s3);
        Inse(s1,s2,s3);
//      if(s1==9271) {
//          printf("%d %d\n",j,0);
//      }
        for(int i=1;i<len-l+1;i++) {
            s1=(s1-s[i-1]*o1+s[i-1]*Mod1)%Mod1;
            s1=(s1*p1+s[i+l-1])%Mod1;
            s2=(s2-s[i-1]*o2+s[i-1]*Mod2)%Mod2;
            s2=(s2*p2+s[i+l-1])%Mod2;
            s3=(s3-s[i-1]*o3+s[i-1]*Mod3)%Mod3;
            s3=(s3*p3+s[i+l-1])%Mod3;
            if(!se(s1,s2,s3)) inse(s1,s2,s3);
            Inse(s1,s2,s3);
//          if(s1==7386) {
//              printf("%d %d\n",j,i);
//          }
        }
    }
    int acht=0;
    for(int i=0;i<Mod1;i++) {
        for(register unsigned int j=0;j<a[i].size();j++) {
            if(a[i][j].c>=m) {
                acht++;
        //      printf("%d %d %d %d\n",i,a[i][j].a,a[i][j].b,a[i][j].c);
            }
        }
    }
    printf("%d\n",acht);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值