字符串哈希

字符串哈希可以帮助我们处理很多与字符串有关的操作,比如快速的判断某个字符串是否存在,或者某两段字符串是否相同,还有著名的字符串匹配问题都可以使用字符串哈希来处理

既然字符串哈希这么神奇,那快来学习它吧!

字符串哈希就是给每个字符串一个哈希值,每个字符串对应的哈希值都是它独一无二的标签(一般情况下)
比如:
h[“aaa”] = 123;
h[“bbb”] = 189;
h[“efwqe”] =3442;

类比10进制,我们规定字符串为p进制:
请添加图片描述
而经过进制计算得到的值再取模一个数就作为该字符串的哈希值

根据经验值,我们取进制p为131或13331 , 取模的数一般取2^64
这里关于取模的问题就有一个小技巧了,因为unsigned的取值上界刚好到2^64,因此直接将存储计算的哈希值变量定义为unsigned就行啦!

那么给你一段字符串,比如:abcdefghijk
你可以很容易计算出从第一个位置到每个位置的字符串的哈希值

//这里字符串从下标为1的地方开始存便于操作
for(int i=1;s[i];i++){
        h[i]=h[i-1]*P+s[i];
    }

其实,在这个计算过程中可以将P次方的值存储好,以便接下来的操作

for(int i=1;s[i];i++){
        p[i]=p[i-1]*P;
        h[i]=h[i-1]*P+s[i]-'0';
    }

已经计算好从第一个位置到每一个位置的字符串哈希值了,那么怎么计算任意两个位置间字符串的哈希值呢

比如要计算该字符串中"def"的哈希值,也就是4~6的字符串
我们可以这样计算:
请添加图片描述

先将h[“abc”]的值乘P^3,也就是让abc向高位移动三位,与"abcdef"的哈希值高位对齐,然后相减

请添加图片描述

就将"abcdef"中的高位"abc"消去啦,得到了"def"的哈希值

可知,算任意两个位置l~r间的字符串哈希值,可以这样算:
unsigned val = h[r] - h[l - 1]*p[r - l + 1]
这里就用到了之前储存好的P次方的值了,p[ k ]就是P的k次方,乘以P的k次方就是向高位移动k位

利用这个就可以去判断两个字符串是否相同,可以处理字符串匹配等问题啦!


下面附上一道acwing的例题:

给定一个长度为 n 的字符串,再给定 m 个询问,每个询问包含四个整数 l1,r1,l2,r2,请你判断 [l1,r1] 和 [l2,r2] 这两个区间所包含的字符串子串是否完全相同。

字符串中只包含大小写英文字母和数字。

输入格式
第一行包含整数 n 和 m,表示字符串长度和询问次数。

第二行包含一个长度为 n 的字符串,字符串中只包含大小写英文字母和数字。

接下来 m 行,每行包含四个整数 l1,r1,l2,r2,表示一次询问所涉及的两个区间。

注意,字符串的位置从 1 开始编号。

输出格式
对于每个询问输出一个结果,如果两个字符串子串完全相同则输出 Yes,否则输出 No。

每个结果占一行。

数据范围
1≤n,m≤105
输入样例:
8 3
aabbaabb
1 3 5 7
1 3 6 8
1 2 1 2
输出样例:
Yes
No
Yes

代码:

#include<iostream>
#include<string>
using namespace std;
const int N=100010,P=131;
unsigned long long p[N],h[N];

int main()
{
    int n,m;
    cin>>n>>m;
    string s,s1;
    cin>>s1;
    s=" ";
    s+=s1;
    p[0]=1;
    for(int i=1;s[i];i++)
    {
        p[i]=p[i-1]*P;
        h[i]=h[i-1]*P+s[i];
        
    }
    
    while(m--)
    {
        int l1,l2,r1,r2;
        unsigned s1,s2;
        scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
        s1=h[r1]-h[l1-1]*p[r1-l1+1];
        s2=h[r2]-h[l2-1]*p[r2-l2+1];
        if(s1==s2)  puts("Yes");
        else puts("No");
        
    }
    
    return 0;
}

学习算法,永无止境,一起加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

听-起风了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值