【转载】字符串Hash & 【题解】好文章

以下内容来源https://blog.csdn.net/Coldfresh/article/details/79476915, 作者Coldfresh

字符串hash是指将一个字符串s映射为一个,使得该整数可以尽可能唯一的代表也就是唯一标识。换言之,如果两个字符的hash值相同那么我们可以认为两者相同。

这里使用的hash策略,便是把一个字符串的每一位赋予权值
假设都是大写的英文字母。
我们设
H[i]代表前i个hash值

那么
H[i]=H[i−1]∗p+val[i]

这里因为是大写英文字母,只有26 中不同的表示,所以这里可以设p为26。(实际上就是把26进制数用10进制数表示)
接下的问题是如果字符串的位数很长,那么很可能所产生的hash值会超过技术表示的范围(以int和long类型为例)。
所以我们可以设置一个数mod,即去余
H[i]=(H[i−1]∗p+val[i])%mod
那么这样可以解决数字太大的问题,但是会造成的另一个问题是产生碰撞,即不同的字符串的hash值可能会相同。
根据相关资料,如果我们把p设置为一个10^7(如10000019)大小,把mod设为一个10^9(如1000000009)大小的数,那么发生碰撞的概率就非常低。
后来我问斌爷,斌爷说把p设为233就够啦,基本上碰撞的概率极小233。

一个延伸问题:如果我得到了一个字符串的hash值,那么怎么得到其中任意一个子串的hash?
首先易知:
H[i...j]=val[i]∗p^(j−i)+val[i+1]∗p^(j−i−1)+val[i+2]∗p^(j−i−2)+...+val[j]∗p^0

显然:
H[j]=H[i−1]∗p^(j−i+1)+H[i...j]

所以:
H[i...j]=(H[j]−H[i−1]∗p^(j−i+1))

这个时候去模:
H[i...j]=((H[j]−H[i−1]∗p^(j−i+1))%mod+mod)%mod

完毕。


以上为转载内容。

如果仍怕冲突,可以开不同种子的Hash表共同判断。

const int maxn = 10005;
int n;
char s[maxn];
struct Hash {
    int p,P,hash1[maxn],hash2[maxn];//hash1[i]是上文的H[i],hash2[i]是上文的p^i
    void init(int p1,int P1) {
        p=p1,P=P1;
        hash2[0]=1;
        for(int i=1; i<=n; i++) {
            hash1[i]=(1LL*hash1[i-1]*p+(s[i]-'a'+1))%P;
            hash2[i]=1LL*hash2[i-1]*p%P;
        }
    }
    int getha(int L,int R) {
        return (hash1[R]-1LL*hash1[L-1]*hash2[R-L+1]%P+P)%P;
    }
};

例题:

好文章:

Description

  nodgd写了一篇文章,自认为这是一篇好文章。nodgd的文章由n个小文字母组成。文章的一个子串指的是文章中的一段连续的字母,子串的长度这一段的字母个数。nodgd在文章中用了排比、对偶、前后照应之类的手法以就有很多个子串是相同或者相近的。为了向大家证明这是一篇好文章,决定给自己的文章进行评分。nodgd 首先确定了一个整数m,然后统计出文有多少个不相同的长度为m的子串,这个数量就是文章的评分。 

  然而,nodgd懒得老老实实计算这个评分了,就把任务丢给了你。

Input

  第一行包含两个整数n,m,表示文章的长度和需要统计的子串长度。   第二行包含一个长度为n的只包含小写字母的字符串。

Output

  输出一行一个整数,表示文章的评分。

Sample Input 1 

【样例1】
5 3
aaaab

【样例2】
9 3
abcabacba

Sample Output 1

【样例1】
2
【样例2】 7

Hint

对于 30%的数据,1 ≤ m ≤ n ≤ 200;
对于 50%的数据,1 ≤ m ≤ n ≤ 2000;
对于另外20%的数据,1 ≤ m ≤ 50 ≤ n ≤ 200000;
对于 100%的数据,1 ≤ m ≤ n ≤ 200000;

双哈希案例,出题人卡数据:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5e5+5, mod1=1e9+7, mod2=1e9+9;
int n,m;
char s[maxn];
//哈希表 
struct Hash {
    int p,P,hash1[maxn],hash2[maxn];
    void init(int p1,int P1) {
        p=p1,P=P1;
        hash2[0]=1;
        for(int i=1; i<=n; i++) {
            hash1[i]=(1LL*hash1[i-1]*p+(s[i]-'a'+1))%P;
            hash2[i]=1LL*hash2[i-1]*p%P;
        }
    }
    int getha(int L,int R) {
        return (hash1[R]-1LL*hash1[L-1]*hash2[R-L+1]%P+P)%P;
    }
} Ha,Hb;
struct Charactor {
    int h1,h2;
    bool operator<(const Charactor& h)const {
        return h1==h.h1?h2<h.h2:h1<h.h1;
    }
} H[maxn];
int main() {
    int i,j,ans=1;
    scanf("%d%d", &n, &m);
    scanf("%s", s+1);
    Ha.init(47, mod1);//不同种子 
    Hb.init(91, mod2);
    for(i=1; i+m-1<=n; i++) {
        H[i].h1=Ha.getha(i,i+m-1);
        H[i].h2=Hb.getha(i,i+m-1);
    }
    sort(H+1,H+1+n-m+1);
    for(i=2; i+m-1<=n; i++)
        ans+=(H[i].h1!=H[i-1].h1||H[i].h2!=H[i-1].h2);//两个哈希中有一个不等即可 
    cout<<ans;
}
View Code
原文地址:https://www.cnblogs.com/de-compass/p/11842714.html