Vision_字符串_字符串哈希(BKDR Hash)

///定义:
/*
        将一个字符串哈希成一个数值,使得这些数值的冲突尽量的小。
    最直白的是sum = s[0] + s[1] + ... + s[len],其中sum就是s的哈希值。
*/

///代码:

/*
**name:BKDR Hash
**来源:官方模板
*/
unsigned int BKDRHash(char *str){
    unsigned int seed = 131;// 31,131,1313,13131,131313 etc..
    unsigned int hash = 0;
    while(*str)P
        hash = hash * seed + (*str++);
    return hash&0x7FFFFFFF;
}

/*
**name:BKDR Hash
**function:处理字符串
**输入参数:字符串s
**输出参数:哈希值
**题意:一个字符串S  问其中有几个子串能满足以下条件:
1、长度为m*L
2、可以被分成m个L长的小串 \每个串都不一样
*/
///BKDRHash字符串哈希的模板题
#include <iostream>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <cstdio>
#include <map>
using namespace std;

typedef unsigned long long LL;///无符号形长整数溢出时自动取模
const int MAX=1e5+7,seed=31;
char s[MAX];
LL base[MAX],h[MAX];
map<LL,int>mp;///map自动去重
LL BKDRHash(int l,int r)///这是求每个子串的哈希值的方法
{
    return h[r]-h[l-1]*base[r-l+1];
}
int main()
{
    int m,l;
    base[0]=1;
    for(int i=1;i<=MAX;i++)base[i]=base[i-1]*seed;///基数
    while(~scanf("%d%d",&m,&l))
    {
        int ans=0;
        scanf("%s",s+1);
        int len=strlen(s+1);
        h[0]=0;
        for(int i=1;i<=len;i++)
            h[i]=h[i-1]*seed+s[i]-'a';///对整个字符串求哈希值
        ///下面通过map来记录充计有多少对符合条件的m*l长度的字符串,个人感觉这里类似于尺取法
        for(int i=1;i<=l&&i+m*l-1<=len;i++)///从i开始连续的m*l长度的子串,注意i<=l跳出循环(因为当i==l时,之后的情况都已经查询了)
        {
            mp.clear();
            for(int j=i;j<=i+m*l-1;j+=l)
            {
                LL x=BKDRHash(j,j+l-1);
                mp[x]++;
            }
            if(mp.size()==m)ans++;
            for(int j=i+m*l;j+l-1<=len;j+=l)
            {
                LL x=BKDRHash(j,j+l-1);
                mp[x]++;
                LL y=BKDRHash(j-m*l,j-m*l+l-1);
                mp[y]--;
                if(mp[y]==0)mp.erase(y);
                if(mp.size()==m)ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

///扩展:
/*
    BKDRHash计算公式的推导
      由一个字符串(比如:ad)得到其哈希值,为了减少碰撞,应该使该字符串中每个字符都参与哈希值计算,
    使其符合雪崩效应,也就是说即使改变字符串中的一个字节,也会对最终的哈希值造成较大的影响。我们直接想到的办
    法就是让字符串中的每个字符相加,得到其和SUM,让SUM作为哈希值,如SUM(ad)= a+d;可是根据ascii码表
    得知a(97)+d(100)=b(98)+c(99),那么发生了碰撞,我们发现直接求和的话会很容易发生碰撞,那么怎么办哪?
    我们可以对字符间的差距进行放大,乘以一个系数:
    SUM(ad) =系数1 * a + 系数2 * d
    SUM(bc)= 系数1 * b + 系数2 * c
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值