字符串匹配——哈希算法

一、算法原理

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

\[H(C)=(c_1b^{m-1}+c_2b^{m-2}+...+c_mb^0) \text~{mod}~h \]

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

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

\[H(S[k + 1, ..., k + m]) = (H(S[k, k + m - 1]) \times b - s_kb^m + s_{k + m})~\text{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} \]


二、代码

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\)\(2^{64}\) ,利用数据溢出,省去了模运算。

posted @   ltign  阅读(105)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示