字符串哈希


子串查找(哈希模板 下标从1开始)

题目链接

题目描述:

(这是一道模板题)
给定一个字符串 A 和一个字符串 B,求 B 在 A 中的出现次数。A 和 B 中的字符均为英语大写字母或小写字母。
A中不同位置出现的 B 可重叠。

输入格式:
输入共两行,分别是字符串 A 和字符串 B。

输出格式:
输出一个整数,表示 B 在 A 中的出现次数。

样例输入:
zyzyzyz
zyz

样例输出:
3

数据范围与提示:
1<=A,B的长度<=10^6,A,B仅包含大小写字母

AC代码详解

#include<bits/stdc++.h>
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为主串
int ans;//ans是我们要求的s2在s1中出现的次数
//pow数组储存的是b的多少次方,sum数组储存的是主串的前i项的哈希值 ,s储存的是模拟串的哈希值
ULL power[N],sum[N],s;

ULL get(int l,int r){
    return sum[r]-sum[l-1]*power[r-l+1];
}

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=1; i+n-1<=m; i++)  //i只需要<=m-n即可,因为我们的主串最多只有m位,我们主串的哈希值最多只计算到sum[m]
    //因为字符串是从下标1开始的,所以这里从i=1开始
        if (get(i,i+n-1)==s)
            ans++;

    printf("%d\n",ans);
    return 0;
}

字符串哈希(下标从0开始)

(题目链接)[https://www.acwing.com/problem/content/843/]

#include<bits/stdc++.h>
using namespace std;
using ULL = unsigned long long;
//typedef unsigned long long ULL;

const int N = 100005;
ULL  p[N],h[N];
string s;
int P=131;//13331也可

// h[i]前i个字符的hash值
// 字符串变成一个p进制数字,体现了字符+顺序,需要确保不同的字符串对应不同的数字
// P = 131 或  13331 Q=2^64,在99%的情况下不会出现冲突
// 使用场景: 两个字符串的子串是否相同

ULL get(int l,int r){
    //if(l == 0) return h[r];
    return h[r]-h[l-1]*p[r-l+1];
}

int main(){
    int n,k,l1,l2,r1,r2;
    string a,b,s;
    cin>>n>>k;
    cin>>s;

    p[0]=1;
    for(int i=0;i<n;i++){ //相比下标从0开始略有不同
        p[i+1] = p[i]*P;   
        if(i==0)
            h[i]=s[i];
        else
            h[i] = h[i-1]*P +s[i];      //前缀和求整个字符串的哈希值
    }

    while(k--){
        cin>>l1>>r1>>l2>>r2;
        if(get(l1-1,r1-1)==get(l2-1,r2-1))  //因为题目要其字符串下标从1开始,所以这里要减一
        cout<<"Yes"<<endl;
        else
        cout<<"No"<<endl;
    }
    return 0;
}

其他例题

28. 实现 strStr()
1062.最长重复子串-弱数据
1044. 最长重复子串-强数据


文章作者: 再也不会
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 再也不会 !
  目录