字符串匹配——哈希算法
1.字符串匹配——哈希算法
一、算法原理
我们不直接比较字符串 \(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}\) ,利用数据溢出,省去了模运算。
完
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)