单词检索

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

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

样例输入
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,均有小写字母组成。

Solution

人生第一道hash。
以前一直知道hash,是把信息转化成一个整数再放到数组里。但一直没有写过。直到上次考了这道。。。
我们可以把字符串看成一个26进制的数,对每一段字符串,我们记录两个信息。
uhash
vhash
如果u一样,就把它们链起来,但如果v又一样的话,就有错误了。但v值mod的大质数因为非常大,所以这个出错的概率是很小的。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
int n,m,ap,k1,u,ans,len,tot;
ll v,k2;
char s[1005];
const int minmod=999973; //小质数
const ll maxmod=(ll)(499999999999993); //大质数
struct ty
{
    int p,cnt;
    ll maxhash;
}Hash[1000005];
int head[1000005],Next[1000005];
void add(int x,int y,ll z) //添加进链表
{
    tot++;
    Hash[tot].maxhash=z;
    Hash[tot].cnt=1;
    Hash[tot].p=y;
    Next[tot]=head[x];
    head[x]=tot;
}
void Find(int art,int u,ll v)
{
    for(int i=head[u];i!=-1;i=Next[i]) //遍历小hash值的一条链
    if(v==Hash[i].maxhash) //大hash值一样,就说明字符串一样
    {
        if(art==Hash[i].p) return; //该文章已经出现过,就不用再加了
        Hash[i].p=art; //更新是哪个文章,前面的文章不可能再出现,所以直接更新
        Hash[i].cnt++;
        return;
    }
    add(u,art,v);
}
int main()
{
    cin>>n>>ap>>m;
    for(int i=0;i<minmod;i++) head[i]=-1;
    k1=1;k2=1;
    for(int i=1;i<m;i++) 
    {
        k1=k1*26%minmod; 
        k2=k2*26%maxmod;
    }
    for(int i=1;i<=n;i++) 
    {
        scanf("%s",s+1);
        len=strlen(s+1);
        u=0;v=0;
        for(int j=1;j<=m;j++) 
        {
            u=(u*26+s[j])%minmod;
            v=(v*26+s[j])%maxmod;
        }
        Find(i,u,v);
        for(int j=m+1;j<=len;j++) 
        {
            u=((u-k1*s[j-m])%minmod+minmod)%minmod;//去掉第一位
            v=((v-k2*s[j-m])%maxmod+maxmod)%maxmod;
            u=(u*26+s[j])%minmod;//添加最后一位
            v=(v*26+s[j])%maxmod;
            Find(i,u,v);
        }
    }
    for(int i=1;i<=tot;i++) 
    if(Hash[i].cnt>=ap) ans++;
    cout<<ans;    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值