zoukankan      html  css  js  c++  java
  • AcWing 841. 字符串哈希

    题目传送门

    一、算法思路

    字符串哈希 (O(n)+O(m))

    全称字符串前缀哈希法,把字符串变成一个(p)进制数字(哈希值),实现不同的字符串映射到不同的数字。并且,用(h[N])记录字符串前(N)个字符的(Hash)值,类似于前缀和。

    作用就是把(O(N))的时间复杂度降为(O(1))。比如本题就是对比任意两段内字符串是不是相同,正常就是类似于一个循环长度次的(substr),其实用(hash)差就能一步搞定!

    栗子:

    str="ABCABCDEYXCACWING";
    h[0]=0;
    h[1]="A"的Hash值;
    h[2]="AB"的Hash值;
    h[3]="ABC"的Hash值;
    h[4]="ABCA"的Hash值;
    

    对形如(X_1X_2X_3...X_{n-1}X_n)的字符串,采用字符 (ASCII)码乘上(P)次方来计算哈希值。

    映射公式:((X_1 imes P^{n-1} +X_2 imes P^{n-2}+...+X_{n-1} imes P^1 + X_n imes P^0) mod Q)

    举个栗子:
    字符串(ABCD)(P=131)
    那么(h[4]=65*131^3+66*131^2+67*131^1+68*131^0)

    (AB)(P=131)
    说是(h[2]=65*131^1+66*131^0)

    我们想要求"(CD)"的(HASH)值,怎么求呢?
    就是 (h[4]-h[2]*131^2)

    注意点:

    1. 任意字符不可以映射成(0),否则会出现不同的字符串都映射成(0)的情况,比如(A),(AA),(AAA)皆为(0)

    2. 冲突问题:通过巧妙设置(P)((131)(13331)) ,(Q)((2^{64}))的值,一般可以理解为不产生冲突(玄学)。
      (Q=2^{64})这个取模动作在代码中没有出现过,这是因为采用了$unsinged long long $,它本身就是(2^{64}),如果超过了这个数字,就直接自动溢出,起到了取模的作用。

    问题是比较不同区间的子串是否相同,就转化为对应的哈希值是否相同。

    求一个字符串的哈希值就类似于构建一维前缀和,求一个字符串的子串哈希值就相当于一维前缀和应用:

    构建: (h[i]=h[i-1] imes P+s[i-1] i∈[1,n]) (h)为前缀和数组,(s[i-1])为字符串数组此位置字符对应的(ASCII)码。

    应用: 查询(l,r)之间部分字符串的(hash=h[r]−h[l−1]×P^{r−l+1})

    二、实现代码

    #include <bits/stdc++.h>
    
    using namespace std;
    typedef unsigned long long ULL;
    
    const int N = 100010; //数组上限范围
    const int P = 131;    //经验,P进制
    int n, m;
    string str;     //字符串
    ULL h[N], p[N]; //h表示某一个前缀的HASH值,h[R]表示的是前R个字母的HASH值,p代表p进制,目前是131进制
    
    //计算部分字符串的HASH值
    ULL get(int l, int r) {
        return h[r] - h[l - 1] * p[r - l + 1];
    }
    
    int main() {
        //读入优化
        ios::sync_with_stdio(false);
        //计算字符串前缀的HASH值
        cin >> n >> m >> str;
    
        p[0] = 1; //初始化
        for (int i = 1; i <= n; i++) {
            //预处理乘方
            p[i] = p[i - 1] * P;//131,131*131,131*131*131.....
            //计算前i个字符的字符串前缀hash值
            h[i] = h[i - 1] * P + str[i - 1];
        }
        //然后根据公式计算两个区间的字符串HASH值是不是相同
        while (m--) {
            int l1, r1, l2, r2;
            cin >> l1 >> r1 >> l2 >> r2;
            if (get(l1, r1) == get(l2, r2))puts("Yes");
            else puts("No");
        }
        return 0;
    }
    
  • 相关阅读:
    代码: 仿淘宝商品详情页左上,图片鼠标浮上去,图片部分区域放大 (页面布局:图片列表)
    !代码:页面布局
    代码: 返回页面顶部 jquery
    !!学习笔记:CSS3动画
    代码: 两列图片瀑布流(一次后台取数据,图片懒加载。下拉后分批显示图片。图片高度未知,当图片onload后才显示容器)
    !学习笔记:前端测试 、前端调试、console 等
    学习笔记:SASS
    !代码:伪类
    代码:css小图标
    学习笔记:ECharts
  • 原文地址:https://www.cnblogs.com/littlehb/p/15293192.html
Copyright © 2011-2022 走看看