【字符串哈希】讲解与例题

字符串哈希算法,目的是将一个字符串转换为一个整数,这个整数仅仅受字符串的内容有关,与字符串的长度没有任何的关系,保障字符串不同,得到的整数不同,字符串相同,得到的整数一定相同。通常将字符串转换为整数的算法成为字符串哈希算法。

通常使用的字符串哈希算法如下:

哈希算法: H a s h [ i ] = H a s h [ i − 1 ] ∗ p + s t r [ i − 1 ] − ′ a ′ Hash[i]=Hash[i-1]*p+str[i-1]- 'a' Hash[i]=Hash[i1]p+str[i1]a

这里p选用的是一个质数,通常可取131313,使用这种算法获得的哈希值能够很大程度上的避免哈希冲突,同时这样计算出的哈希值能通过前缀和的思想,推演出任意连续子串的哈希值,操作如下:

image-20211223153116702

可以看到,通过存储的Hash数组与p的次方,就能够求出任意子串的哈希值,通过这种方法就可以判断任意子串是否出现过。为了更方便的计算p的任意次方,我们预先处理p的幂将其存入数组中,这样就可以快速求得一个子串的哈希值。

公式为:定义 h a s h [ i ] hash[i] hash[i]为索引为 [ 0 , i ] [0,i] [0,i]子串的哈希值,定义 p [ i ] p[i] p[i]表示 p i p^i pi的值,定义所求哈希值子串下标范围是 [ j , i ] [j,i] [j,i],那么该子串哈希值为: h a s h = h a s h [ i ] − p [ i − j + 1 ] ∗ h a s h [ j − 1 ] hash = hash[i]-p[i-j+1]*hash[j-1] hash=hash[i]p[ij+1]hash[j1]

那么为什么不使用String直接作为key存入HashMap而是用这种方法计算哈希值呢?

String作为key存入hashMap,每次存入一个数,都会调用equals,HashCode方法,这两个方法均是 O ( N ) O(N) O(N)的时间复杂度,但是如果存入的是int整数类型,因为int的哈希值就是该值本身,因此存入hashMap中仅需要 O ( 1 ) O(1) O(1)的时间复杂度,大大地提高了运行效率。


1316. 不同的循环子字符串为例,使用字符串哈希解决

image-20220217150639084

首先通过递推公式将 [ 0 , i ] [0,i] [0,i]的哈希值存放在 h a s h [ i ] hash[i] hash[i]数组中,同时将 p i p^i pi存放在 p [ i ] p[i] p[i]数组中方便使用,通过两层for循环遍历所有子串(符合题意子串长度一定是偶数),找到中间点mid然后判断mid两侧哈希值是否相同,如果相同就将其哈希值存入Set中用于去重,最终Set中的元素个数就是我们所求的子串数量。

class Solution {
    final int P = 131313;
    long []p;
    long []hash;
    public long getHash(String txt,int l,int r){
        if(l==0){
            return hash[r+1];
        }
        return hash[r + 1] - getHash(txt, 0, l - 1) * p[r - l + 1];
    }
    public int distinctEchoSubstrings(String text) {
        int n = text.length();
        p=new long[n+1];
        hash=new long[n+1];
        Set<Long> ans=new HashSet<>();
        p[0]=1;
        for(int i=1;i<=n;++i){
            hash[i]=hash[i-1]*P+text.charAt(i-1);
            p[i]=p[i-1]*P;
        }
        for(int i=0;i<n-1;++i){
            for(int j=i+1;j<n;j+=2){
                int m=i+(j-i)/2;
                long hashLeft=getHash(text,i,m);
                long hashRight=getHash(text,m+1,j);
                if(hashRight==hashLeft){
                    ans.add(hashLeft);
                }
            }
        }
        return ans.size();
    }
}

187. 重复的DNA序列为例,使用字符串哈希快速解题。

image-20211223153647269

  • 可能你觉得这题使用滑动窗口也同样能过,确实是这样,所求序列长度固定为10,那么窗口滑动+字符串比较运算量一共就 1 0 6 10^6 106次,能够接受。但是如果数据量特别大,所求序列的长度为100或1000,那么每一次滑动都需要比较100、1000个字符,这样就特别容易超时。因此这题使用字符串哈希算法
  • 我们定义一个 h a s h hash hash数组,对于下标 i i i 存放子串 [ 0... i − 1 0 ...i-1 0...i1]子串的哈希值,同时选用质数 P = 131313 P=131313 P=131313,维护一个质数的次方数组p
class Solution {
    public List<String> findRepeatedDnaSequences(String s) {
        Map<Long,Integer>map=new HashMap<>();
        List<String>ans=new ArrayList<>();
        int n=s.length();
        long []hash=new long[n+1];
        long []p=new long[n+1];
        //已知答案的长度
        int strLen=10;
        p[0]=1;
        final int P=131313;
        for(int i=1;i<=n;++i){
            hash[i]=hash[i-1]*P+s.charAt(i-1);
            p[i]=p[i-1]*P;
        }
        //开始遍历字符
        //i为开头的下一个字符
        for(int i=1;i+strLen-1<=n;++i){
            //j为结束的下一个字符
            int j=i+strLen-1;
            long curHash=hash[j]-hash[i-1]*p[strLen];
            int time=map.getOrDefault(curHash,0);
            if(time==1){
                ans.add(s.substring(i-1,j));
            }
            map.put(curHash,time+1);
        }
        return ans;
    }
}
  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
字符串哈希算法是一种将字符串映射为数字的算法,常用于字符串的比较和匹配。在C++中,可以使用字符串哈希算法来加速字符串的比较操作。 引用\[1\]中的代码示例展示了一个使用字符串哈希算法的C++代码。该代码使用了前缀和数组和字符串数组来存储字符串,并通过计算哈希值来比较两个子串是否相等。其中,哈希值的计算使用了前缀和数组和幂运算。 引用\[2\]中的解释指出,使用字符串哈希的目的是为了比较字符串时不直接比较字符串本身,而是比较它们对应映射的数字。这样可以将子串的哈希值的时间复杂度降低到O(1),从而节省时间。 引用\[3\]中的代码示例也展示了一个使用字符串哈希算法的C++代码。该代码使用了前缀和数组和字符串数组来存储字符串,并通过计算哈希值来比较两个子串是否相等。与引用\[1\]中的代码类似,哈希值的计算也使用了前缀和数组和幂运算。 综上所述,字符串哈希算法是一种将字符串映射为数字的算法,常用于字符串的比较和匹配。在C++中,可以使用前缀和数组和幂运算来计算字符串哈希值,并通过比较哈希值来判断两个子串是否相等。 #### 引用[.reference_title] - *1* [C++算法题 # 33 字符串哈希](https://blog.csdn.net/weixin_44536804/article/details/123425533)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [字符串哈希(c++)](https://blog.csdn.net/qq_41829492/article/details/120980055)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [AcWing 841. 字符串哈希(C++算法)](https://blog.csdn.net/YSA__/article/details/108453403)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

秋刀鱼与猫_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值