【基础算法】字符串哈希

🌹作者:云小逸
📝个人主页:云小逸的主页
📝Github:云小逸的Github
🤟motto:要敢于一个人默默的面对自己,强大自己才是核心。不要等到什么都没有了,才下定决心去做。种一颗树,最好的时间是十年前,其次就是现在!学会自己和解,与过去和解,努力爱自己。==希望春天来之前,我们一起面朝大海,春暖花开!==🤟
👏专栏:C++👏 👏专栏:Java语言👏👏专栏:Linux学习👏
👏专栏:C语言初阶👏👏专栏:数据结构👏👏专栏:备战蓝桥杯👏


前言

今天这篇文章讲解的是哈希的另一种题型:字符串前缀哈希法,希望我的讲解你可以喜欢,谢谢。
——————————————————————————————

字符串前缀哈希法:

核心思想:

字符串前缀哈希法是一种比较特殊的哈希方法,它是先预处理出所有字符串前缀的哈希,利用哈希数组进行存储,下标是0开始的,h[0]是等于0的。
例如:
字符串“abcabcde”
h[0]=0;
h[1]="a"的hash(哈希)值;
h[2]="ab"的hash值;
h[3]="abc"的hash值;
……

如何定义某一个前缀的哈希值?(将字符串转换为数字)

将字符串看成P进制的数,然后将P进制的数转换为十进制的数字:
在这里插入图片描述
这样转换成十进制的数字,可能非常大,因为字符串可能有10到20个,这转换后就是很大很大的数字,容易溢出和出现错误,因此可以对Q进行取模,使其映射到【0,Q-1】;

1.不可以映射成0:

如:
a----------0;
aa--------00,
这样就不对了,两个不同的字符串映射成一样的结果,造成冲突

2.会存在冲突:

将P和Q设定的非常好,就可以极大概率避免冲突(99.999%):
P=131or13331
Q=264;
这里取Q=264,这里可以直接定义哈希数组为unsigned long long ,它会使超过264的数溢出,溢出的值就等价于对264取模。

好处:利用前面算的前缀哈希可以求出任意一段的子串的哈希值。

在这里插入图片描述
如图上面的这个图:
我们已知:h[R]和h[L-1]的哈希值,怎么求出L到R的哈希值?
在这里插入图片描述
上面我们不是假设了字符串为以P为进制的数字,则右边是高位,左边是低位,
h[i]=h[i−1]×P+hash(s[i]);
故hash(s[l…r])=h[r]−h[l−1]×Pr−l+1;

例题:

题目:

给定一个长度为 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 <algorithm>

using namespace std;

typedef unsigned long long ULL;//将P和Q设定的非常好,就可以极大概率避免冲突(99.999%):
                               //P=131or13331
const int N = 100010, P = 131; //Q=2^64^;
                               //这里取Q=2^64^,这里可以直接定义哈希数组为unsigned long long ,
                               //它会使超过2^64^的数溢出,溢出的值就等价于对2^64^取模。
int n, m;
char str[N];
ULL h[N], p[N];//p[N]是存放要乘以p的多次方

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

int main()
{
    scanf("%d%d", &n, &m);
    scanf("%s", str + 1);//因为如果直接用 scanf("%s",str); 的话,就会出现一个问题:
                         //scanf函数遇到空格或TAB,就会停下来。所以用指针的方式就可以防止这种情况发生。
                        //输入str第一个元素之后的字符串,给str[1]赋值
    p[0] = 1;
    for (int i = 1; i <= n; i ++ )
    {
        h[i] = h[i - 1] * P + str[i];
        p[i] = p[i - 1] * P;
    }

    while (m -- )
    {
        int l1, r1, l2, r2;
        scanf("%d%d%d%d", &l1, &r1, &l2, &r2);

        if (get(l1, r1) == get(l2, r2)) puts("Yes");
        else puts("No");
    }

    return 0;
}

代码解析:

1.解决冲突:

typedef unsigned long long ULL;//将P和Q设定的非常好,就可以极大概率避免冲突(99.999%):
                               //P=131or13331
const int N = 100010, P = 131; //Q=2^64^;
                               //这里取Q=2^64^,这里可以直接定义哈希数组为unsigned long long ,
                               //它会使超过2^64^的数溢出,溢出的值就等价于对2^64^取模。
int n, m;
char str[N];
ULL h[N], p[N];//p[N]是存放要乘以p的多次方

2.scanf(“%s”, str + 1);

 scanf("%s", str + 1);//因为如果直接用 scanf("%s",str); 的话,就会出现一个问题:
                         //scanf函数遇到空格或TAB,就会停下来。所以用指针的方式就可以防止这种情况发生。
                        //输入str第一个元素之后的字符串,给str[1]赋值

最后

十分感谢你可以耐着性子把它读完和我可以坚持写到这里,送几句话,对你,也对我:

1.莫言在《晚熟的人》当中说:真正的强大不是忘记,而是接受接受分道扬镳,接受世事无常,接受孤独挫败,接受突如其来的无力感,接受自己的不完美,接受困惑、不安、焦虑和遗憾,调整自己的状态,找到继续前行的力量,成为更好的自己。是的,与其苦苦的想忘记,不如坦然接受。

2.接受你最闪耀的时候,不骄傲;接受你最糟糕的时候,不气馁;接受你最平淡无奇的时候,不放弃。正如王朔对女儿说的:内心强大到混蛋,比什么都重要。

3.三毛曾说过:“给自己时间,不要焦急,一步一步来,一日一日过,请相信生命的韧性是惊人的,跟自己的心去合作,不要放弃对自己的爱护。

4.当你的能力还驾驭不了你的目标时,你就应该沉下心来历练。祛除杂念,不好高骛远,也不轻言放弃。信手拈来的从容都是厚积薄发的沉淀,找到一个准确的定位,认真打磨自己,慢慢就能变得波澜不惊,在喧嚣中宁静致远。但愿你心安,向内探求不停,做有心的蓄积者,沉淀自己,升华自己。

5.有心栽花花不开,无心插柳柳成荫。生活有时候很有意思,你越是用力证明,越感觉疲惫。越是对一件事的结果产生执念,就越反着来。太过用力,本身就是一种消耗,反而适得其反,用温柔的力量,反而能厚积薄发,就像发条上的太紧容易断,该放松时放松,该努力时努力,张弛有度才刚刚好。不骄不躁,抚平心态,才能看到更好的阳光。不要一味地追求太紧绷的用力,人生最坏的结果不过是大器晚成。

最后如果觉得我写的还不错,请不要忘记点赞✌,收藏✌,加关注✌哦(。・ω・。)

愿我们一起加油,奔向更美好的未来,愿我们从懵懵懂懂的一枚菜鸟逐渐成为大佬。加油,为自己点赞!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: C语言中可以使用ECC(椭圆曲线加密)算法来加密字符串。ECC是一种非对称加密算法,它基于椭圆曲线上的点运算实现。 首先,我们需要定义一个椭圆曲线,通常使用椭圆曲线的方程 y^2 = x^3 + ax + b 来表示。选择合适的曲线参数a和b。 然后,我们需要选择一个基点G作为椭圆曲线上的一个点,及私钥d,将公钥Q = dG生成。私钥d是一个随机的大整数。 现在,我们有了曲线参数和公钥,可以进行加密了。 首先,将待加密的字符串转换为一个大整数m。可以使用ASCII码将字符转换为整数。 接下来,我们选择一个随机数k,并用kG生成一个点。 然后,计算该点的x坐标r = (kG).x,并计算消息m的哈希值h。 接着,我们需要计算椭圆曲线上的一个点S,公式为 S = k^(-1)(m + rd)。 现在,我们将(r,S)这对数字作为加密结果输出。 解密时,我们使用私钥d和加密结果(r,S)进行计算。 首先,计算点P = rG,然后根据公式 m = (S - dP).x 来解密消息m。 这样,我们就使用ECC算法成功加密和解密了字符串。 需要注意的是,在实际应用中,我们还需要选择适当的曲线参数、保护私钥安全、以及使用更复杂的算法来提高加密强度。同时,还需要考虑实现的效率和性能等因素。 ### 回答2: C语言中的ECC(椭圆曲线加密)是一种非对称加密算法,它使用椭圆曲线上的离散对数问题作为基础,用于加密和解密数据。 在C语言中实现ECC加密字符串的步骤如下: 1. 首先,需要定义椭圆曲线的参数。包括椭圆曲线的方程、基点、模数等参数。 2. 然后,生成一个私钥(private key)和一个公钥(public key)。私钥是一个随机数,公钥是通过私钥和椭圆曲线参数计算得到的。 3. 待加密的字符串需要转换为对应的字节数组。 4. 对字节数组进行填充或者处理,以确保其长度符合椭圆曲线的要求。 5. 选择一个随机数作为加密的参数。 6. 使用公钥和随机数,利用椭圆曲线的点乘法运算得到加密后的点。 7. 对得到的点进行编码,生成加密后的字符串。可以使用Base64等编码方式。 8. 对加密后的字符串进行传输或保存。 9. 接收方使用其私钥和椭圆曲线参数,进行解密。先将加密后的字符串解码,得到加密点。然后使用私钥和加密点,再进行一次点乘法运算,得到解密后的字节数组。 10. 最后,将解密后的字节数组转换为字符串即可获取原始字符串。 通过以上步骤,我们就可以在C语言中使用ECC加密字符串。需要注意的是,为了安全性,私钥应该保密保存,而公钥可以公开给其他人使用。同时,椭圆曲线的选择也需要遵循安全性和效率的原则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

云小逸

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

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

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

打赏作者

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

抵扣说明:

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

余额充值