单词检索


小可可是学校图书馆的管理员,现在他接手了一个十分棘手的任务。由于学校需要一些材料,校长需要在文章中检
索一些信息。校长一共给了小可可N篇文章,每篇文章为一个字符串。现在,校长需要他找到这样的单词,它至少
在这N篇文章中的M篇文章里出现过,且单词长度为L。可是,工作量十分庞大,但校长又急需小可可完成这项任务
。现在他向你求助,需要你编写程序完成这项艰巨的任务
Input
第1行3个正整数N,M,L,表示文章的数目,单词至少出现在M篇文章中和每个单词的长度。
接下来N行,每行一个字符串,表示一篇文章。
1≤N,M≤2000,L≤1000。每篇文章长度不大于1000,均有小写字母组成
Output
仅一行,表示满足检索条件的单词数。

Sample Input
3 2 2
noip
istudycpp
imacppstudent
Sample Output
5
//这5个单词分别为:st,tu,ud,pp,cp。

 Sol:对于一些数据精心构造过的,最好写成双hash

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
typedef long long LL;
const int MaxN=2005;
const int MaxM=1000005;
const int MinMod=999973;
const LL MaxMod=499999999999993ll;
struct Node
{
 int no,cnt;//no为单词所在文章编号,cnt为单词出现的次数 
 LL s;//s为单词的hashh值 
 int next;//用开散列解决冲突 
}h[MaxM];
int N,M,L,tot,ans;
int first[MaxM];
char str[MaxN][MaxN];
void add(int u,int x,LL s)
//将在第x篇文章中出现的短hashh值为u,长hashh值为s的单词加入hashh表 
{
 ++tot;
 h[tot].s=s;
 h[tot].cnt=1;
 h[tot].no=x;
 h[tot].next=first[u];
 first[u]=tot;
}
void hashh(int x,int u,LL s)
{int i;
 for(i=first[u];i!=0;i=h[i].next)
   if (s==h[i].s)//在hashh表出现过 
     {
     if (h[i].no==x) return;
     //也是在第x篇文章中出现,就不用管了 
      h[i].no=x;
      h[i].cnt++;//出现的次数加1 
      return;
     }
 add(u,x,s);
}
void init()
{
    int i;
    scanf("%d%d%d",&N,&M,&L);
    for(i=1;i<=N;i++)
    scanf("%s",&str[i]);
    ans=0;
}
void work()
{int i,j,u,k,k1;
 LL s,k2;
 k1=k2=1;
 for(j=0;j<L-1;j++)
 {
    k1=k1*26%MinMod;//数量级 
    k2=k2*26%MaxMod;
 }
 for(i=1;i<=N;i++)
 {
    u=s=0;
    for (j=0;j<L;j++)
    {
       u=(u*26+str[i][j])%MinMod;//短hashh值 
       s=(s*26+str[i][j])%MaxMod;//长hashh值 
    }
    hashh(i,u,s);
    k=strlen(str[i]);
    for(j=L;j<k;j++)
      {
       u=((u-str[i][j-L]*k1)%MinMod+MinMod)%MinMod;//前面去一位后面加一位 
       s=((s-str[i][j-L]*k2)%MaxMod+MaxMod)%MaxMod;
       u=(u*26+str[i][j])%MinMod;
       s=(s*26+str[i][j])%MaxMod;
       hashh(i,u,s);
      }
   }
}
void output()
{
 int i;
 for(i=1;i<=tot;i++)
   if(h[i].cnt>=M) ++ans;
 printf("%d
",ans);
}
int main(void)
{
 init();
 work();
 output();
 return 0;
}

  

单Hash的做法,事实上有很大可能出错的,某两个不同的字符串hash出来的值是一样的。

#include<bits/stdc++.h>
#define ll long long 
#define base 31
using namespace std;
const int mod=1e9+7,maxm=2e6+10;
int n,m,l,ans;
int f[maxm],g[maxm];
ll p[1010]={1},hash1[maxm];
char s[1010];
inline int add(int x)
{
    int tmp=x%maxm;
    while(hash1[tmp]&&hash1[tmp]!=x)tmp=(tmp+1)%maxm;
    return tmp;
}
int main()
{
    scanf("%d%d%d",&n,&m,&l);
    for(int i=1;i<l;i++)
        p[i]=p[i-1]*base%mod;
    while(n--)
    {
        scanf("%s",s+1);
        int len=strlen(s+1);
        ll sum=0;
        for(int i=1;i<l;i++)
            sum=(sum+p[l-1-i]*s[i])%mod;
        for(int i=l;i<=len;i++)
        {
            sum=(sum+mod-p[l-1]*s[i-l]%mod)%mod;
            sum=(sum*base+s[i])%mod;
            int k=add(sum);
            hash1[k]=sum;
            if(g[k]!=n)
            {
                if(++f[k]==m)ans++;
                g[k]=n;
            }
        }
    }
    printf("%d",ans);
    return 0;
}

  

 

  

原文地址:https://www.cnblogs.com/cutemush/p/12297616.html