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