字符串匹配——哈希算法

本文详细介绍了哈希算法的工作原理,包括计算哈希值的方法以及滚动哈希的优化技巧,还提供了C++代码示例,展示了如何使用素数哈希基数和数据溢出简化模运算。
摘要由CSDN通过智能技术生成

一、算法原理

我们不直接比较字符串 S S S 的字串和模式串 T T T 是否相等,而是比较二者的哈希值。
设字符串 S S S 的长度为 l l l,字符串 T T T 的长度为 m m m
取两个互素的常数 b b b h h h ( l < b < h l < b < h l<b<h),设字符串 C = c 1 c 2 . . . c m C = c_1c_2...c_m C=c1c2...cm,则哈希函数为:

H ( C ) = ( c 1 b m − 1 + c 2 b m − 2 + . . . + c m b 0 )   m o d   h H(C)=(c_1b^{m-1}+c_2b^{m-2}+...+c_mb^0) \text~{mod}~h H(C)=(c1bm1+c2bm2+...+cmb0) mod h

其中 b b b 是哈希基数,相当于把字符串看作 b b b 进制数(,哈希函数就是将 b b b 进制转换为十进制)。

滚动哈希:这是一种优化技巧,用于优化字符串的匹配。
字符串 S S S 从下标 k + 1 k + 1 k+1 开始的长度为 m m m 的子串,它的哈希值为 H ( S [ k + 1 , k + m ] ) H(S[k + 1, k + m]) H(S[k+1,k+m]);字符串 S S S 从下标 k k k 开始的长度为 m m m 的子串,它的哈希值为 H ( S [ k , k + m − 1 ] ) H(S[k, k + m - 1]) H(S[k,k+m1])。二者的关系为:

H ( S [ k + 1 , . . . , k + m ] ) = ( H ( S [ k , k + m − 1 ] ) × b − s k b m + s k + m )  mod  h H(S[k + 1, ..., k + m]) = (H(S[k, k + m - 1]) \times b - s_kb^m + s_{k + m})~\text{mod}~h H(S[k+1,...,k+m])=(H(S[k,k+m1])×bskbm+sk+m) mod h

证明:

H ( S [ k + 1 , . . . , k + m ] ) = ( s k + 1 b m − 1 + s k + 2 b m − 2 + . . . + s k + m b 0 )   m o d   h H ( S [ k , k + m − 1 ] ) = ( s k b m − 1 + s k + 1 b m − 2 + . . . + s k + m − 1 b 0 )   m o d   h H ( S [ k , k + m − 1 ] ) × b = ( s k b m + s k + 1 b m − 1 + . . . + s k + m − 1 b 1 )   m o d   h H ( S [ k , k + m − 1 ] ) × b − s k b m + s k + m = ( s k + 1 b m − 1 + s k + 2 b m − 2 + . . . + s k + m b 0 )   m o d   h ∴ H ( S [ k + 1 , . . . , k + m ] ) = ( H ( S [ k , k + m − 1 ] ) × b − s k b m + s k + m )  mod  h \begin{split} H(S[k + 1, ..., k + m])&=(s_{k+1}b^{m-1}+s_{k+2}b^{m-2}+...+s_{k+m}b^0) \text~{mod}~h\\ \\ H(S[k, k + m - 1])&=(s_kb^{m-1}+s_{k+1}b^{m-2}+...+s_{k+m-1}b^0) \text~{mod}~h \\ H(S[k, k + m - 1]) \times b&=(s_kb^{m}+s_{k+1}b^{m-1}+...+s_{k+m-1}b^1) \text~{mod}~h \\ H(S[k, k + m - 1]) \times b - s_kb^m + s_{k + m}&=(s_{k+1}b^{m-1}+s_{k+2}b^{m-2}+...+s_{k+m}b^0) \text~{mod}~h\\ \\ \therefore H(S[k + 1, ..., k + m]) = (H(S[k, &k + m - 1]) \times b - s_kb^m + s_{k + m})~\text{mod}~h \end{split} H(S[k+1,...,k+m])H(S[k,k+m1])H(S[k,k+m1])×bH(S[k,k+m1])×bskbm+sk+mH(S[k+1,...,k+m])=(H(S[k,=(sk+1bm1+sk+2bm2+...+sk+mb0) mod h=(skbm1+sk+1bm2+...+sk+m1b0) mod h=(skbm+sk+1bm1+...+sk+m1b1) mod h=(sk+1bm1+sk+2bm2+...+sk+mb0) mod hk+m1])×bskbm+sk+m) mod h


二、代码

typedef unsigned long long ull;

const ull B = 1e9 + 7;

// 计算字符串a的哈希值 
ull h(string a)
{
	ull ah = 0;
	for (int i = 0; i < a.size(); i++)    ah = ah * B + a[i];
	return ah;
}

// a是否在b中出现 
bool locate(string a, string b)
{
	// a比b长,a不可能在b中 
	int al = a.size(), bl = b.size();
	if (al > bl)    return false;
	
	// 计算B的al次方
	ull t = 1;
	for (int i = 0; i < al; i++)     t *= B;
	
	// 计算a和b长度为al的前缀对应的哈希值
	ull ah = 0, bh = 0;
	ah = h(a), bh = h(b); 
	
	// 右移窗口,更新哈希值并判断
	if (bh == ah)    return true;
	for (int i = 1; i + al < bl; i++)
	{
		bh = bh * B + b[i + al] - b[i] * t;
		if (bh == ah)    return true;
	}
	return false;
}

注:

  • 1、这里哈希基数为素数 1e9 + 7,它还有一个孪生素数 1e9 + 9
  • 2、该代码取 h h h 2 64 2^{64} 264 ,利用数据溢出,省去了模运算。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值