字符串哈希

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

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

字符串哈希就是给每个字符串一个哈希值,每个字符串对应的哈希值都是它独一无二的标签(一般情况下)
比如:
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;
}

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

### C++ 字符串哈希练习题 #### 一、字符串哈希基础概念 字符串哈希是一种将字符串映射为整数的技术,通过这种方式可以快速比较两个字符串是否相等或查找子串等问题。通常情况下,会选取一个基数`base`来模拟多进制转换过程,并利用模运算防止数值溢出。 #### 二、经典例题解析 ##### AcWing 841. 字符串哈希[^1] 此题作为一道典型的字符串哈希入门题目,主要考察如何构建并应用简单的字符串哈希函数处理给定问题。对于长度较大的文本匹配场景尤为适用。 ```cpp const int N = ...; unsigned long long h[N], p[N]; // 初始化p数组, 计算以base为底的幂次方表 void init() { p[0] = 1; for (int i = 1; i < N; ++i) p[i] = p[i - 1] * base % mod; } // 获取区间[l,r]对应的hash值 unsigned long long get(int l, int r) { return (h[r] - h[l - 1] * p[r - l + 1]) % mod; } ``` 上述代码片段展示了基于前缀和的方式预处理字符串哈希值的方法,其中`mod`用于取余操作确保不会发生越界错误;而`get()`方法则实现了任意区间的哈希查询功能。 #### 三、实战演练建议 为了更好地掌握这一知识点,推荐尝试以下几类具有代表性的习题: - **单模式串匹配**:如POJ 2774 DNA Sequence,这类题目往往涉及在一个较长的目标序列中定位某个特定模式串的位置。 - **多重模式串匹配**:例如HDU 3746 Caesar Cipher Plus,在此类挑战里可能需要同时考虑多个不同长度的模式串与目标串之间的关系。 - **最长公共前后缀/回文串检测**:像Codeforces Round #XXX Div. Y Problem Z这样的竞赛真题也值得深入研究,它们能够很好地锻炼选手灵活运用字符串哈希技巧的能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

听-起风了

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

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

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

打赏作者

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

抵扣说明:

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

余额充值