Hash——字符串匹配(求s1在s2中出现的次数)

题目描述:

这是一道模板题。

给定一个字符串 A 和一个字符串 B ,求 B 在 A  中的出现次数。A 和 B中的字符均为英语大写字母。

求A 在 B 中出现了几次。(可重叠)

样例输入:

3

BAPC

BAPC

AZA

AZAZAZA

VERDI

AVERDXIVYERDIAN

样例输出:

1

3

0

首先要知道什么是字符串hash(滚动哈希):

  单哈希可以O(m)的时间计算长度为m的字符串的哈希值,但对于本题,总的时间复杂度没有改观。时间会爆。

  这时我们就需要一个叫做滚动哈希的优化技巧。

  我们选取两个合适的互质常数b和h(b<h),假设字符串C=c1c2……cm,那么我们定义哈希函数:H(C)=(c1bm-1+c2bm-2+……+cmb0) mod h 。

  正常数字是十进制的,这里b是基数,相当于把字符串看做是b进制数。

  这一过程是递推计算的,设H(C,k)为前k个字符构成的字符串的哈希值,则:(以下均不考虑取模的情况)

  H(C,k+1)=H(C,k)× b + ck+1

  字符串哈希,通常题目要求的是判断主串的一段字符串与另一个匹配串是否匹配,即判断字符C=c1c2……cm从位置k+1开始的长度为n的子串C'=ck+1ck+2……ck+n的哈希值与另一匹配串S=s1s2……sn的哈希值是否相等,则:

  H(C')=H(C,k+n) - H(C,k) × bn

  于是我们只要预求得b,就能在O(1)时间内得到任意字符串的字符串的子串哈希值,从而完成字符串匹配,那么上述字符串匹配问题的算法复杂度就为O(n+m)。

  在实现算法时,可以利用32位或64位无符号整数计算hash值(如:unsigned long long),并取h=232或h=264,通过自然溢出省去取模运算。

                                                                              ——By《一本通》


那么本题就可以用上述方式AC了(书上代码有bug,需自己改动)

 AC代码如下:

#include<cstring>
#include<cstdio>
using namespace std; #define ULL unsigned long long #define K 103 int N; char s1[1000005], s2[1000005]; ULL f[1000005],l1,l2,t; ULL a[1000005]; ULL get(int x,int y) { return f[y]-f[x-1]*a[y-x+1]; } int main() { //freopen("字符串匹配(求s1在s2中出现的次数).in","r",stdin); //freopen("字符串匹配(求s1在s2中出现的次数).out","w",stdout); scanf("%d",&N); a[0]=1; for(int i=1;i<=1000000;++i)//预处理出a^n a[i]=a[i-1]*K; for(int i=1;i<=N;++i) { int ans(0);t=0; scanf("%s%s",s2+1,s1+1); l1=strlen(s1+1);l2=strlen(s2+1); for(int j=1;j<=l1;++j) f[j]=f[j-1]*K+(s1[j]-'A');//计算主串的滚动哈希值 for(int j=1;j<=l2;++j) t=t*K+(s2[j]-'A');//计算匹配串的哈希值 for (int j=1;j+l2-1<=l1;++j) { if(get(j,j+l2-1)==t)//枚举起点为i,长度为n的子串,判断与匹配串是否匹配 ans++; } printf("%d\n",ans);//输出 } return 0; }

 

posted @ 2018-09-04 21:52  落笔映惆怅丶  阅读(661)  评论(0编辑  收藏  举报