子串查找
题目描述:
这是一道模板题。
给定一个字符串 A 和一个字符串 B,求 B 在 A 中的出现次数。A 和 B 中的字符均为英语大写字母或小写字母。
A中不同位置出现的 B 可重叠。
输入格式:
输入共两行,分别是字符串 A 和字符串 B。
输出格式:
输出一个整数,表示 B 在 A 中的出现次数。
样例输入:
zyzyzyz
zyz
样例输出:
3
数据范围与提示:
1<=A,B的长度<=10^6,A,B仅包含大小写字母
我看的是信息学奥赛一本通 提高篇61页,有详细讲解,可以参考一下。
AC代码详解:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef unsigned long long ULL;//定义无符号长整型
//无符号长整型的输出格式为%llu,取值范围为0~2^64-1
//无符号数自然溢出的结果也为正整数
const ULL b = 1e9+7;//b取1e9+7的时候几乎不可能发生冲突,可以记住
const int N = 1000005;
char s1[N], s2[N];//s1为主串,s2为模拟串
int ans;//ans是我们要求的s2在s1中出现的次数
//pow数组储存的是b的多少次方,sum数组储存的是主串的前i项的哈希值 ,s储存的是模拟串的哈希值
ULL power[N],sum[N],s;
int main()
{
int i;
scanf("%s%s",s1+1,s2+1); /*输入字符串的下标从1开始*/
power[0]=1;
//预处理一下b的n次方,即打表
for (i=1; i<1000000; i++)
power[i]=power[i-1]*b; //b的1次方,2次方...n次方,让它自然溢出
ULL m=strlen(s1+1), n=strlen(s2+1);//求一下s1和s2字符串的长度
sum[0]=0;
for (i=1; i<=m; i++) /*主串的哈希值*/
//主串的哈希值必须要用数组存起来,因为主串的每一段都要与模拟串的哈希值做对比
sum[i]=sum[i-1]*b+s1[i];//将主串的每一段都转换成b进制数
for (i=1; i<=n; i++)
s=s*b+s2[i]; /*s定义成全局变量(s的初始值为0),s为模拟串的哈希值*/
for (i=0; i<=m-n; i++)//i只需要<=m-n即可,因为我们的主串最多只有m位,我们主串的哈希值最多只计算到sum[m]
//i要从0开始,因为对于前n项我们也要比较
if (s==sum[i+n]-sum[i]*power[n])
ans++;
printf("%d\n",ans);
return 0;
}