背景
RK算法是通过比较字符串的哈希值来实现字符串匹配,其核心思想是把字符串转换为 N 进制的数字。
本文主要是学习和记录下其实现思想,在日常项目中字符串查找使用 string 的 find 函数即可。
文本转数字
①.10进制转换
string s = "1234";
int size = s.size();
int res = 0;
for (int i = 0; i < size; ++i)
{
res = res * 10 + (s[i] - '0');
}
②.N 进制转换
对于 10 进制数字,原数字通过乘 10 可以扩展一位,然后在末尾添加新值,同理对于 N 进制,代码如下:
int B ;// 进制
int curNumber ;//当前值
int addNumber ;//要扩展的末位值
curNumber = curNumber * B + addNumber;//末尾扩展一位
字符串匹配 RK 算法
①.概述
由于ASCII 码有256位,因此可以把 ASCII 码表示的字符串转换为 256 位的数字,其实际结果是一个很大的数字,超出了 int 的表示范围,通常会采用求模运算把结果限定在一个固定的范围。
②.代码示例
int rabinKarp(const string &txt, const string &pat)
{
int patSize = pat.size();//位数
int B = 256;//进制
long Q = 165859167;//用作求模,通常找一个较大到素数
long long patHash = 0;//模式字符串的哈希值
for (int i = 0; i < patSize; ++i)
{
patHash = (( patHash * B ) % Q + pat[i] % Q ) % Q;// (A+B)%Q =(A%Q+B%Q)%Q
}
long long BH = 1;//提前计算 B 的 patSize -1 次方,用于移除最高位
for (int i = 0; i < patSize-1; i++)
{
BH = (BH * B) % Q;
}
long long currentHash = 0;//当前匹配字符串到哈希值
int txtSize = txt.size();
int left = 0, right = 0;//遍历每个子串
while ( right < txtSize )
{
currentHash = (( currentHash * B ) % Q + txt[right] % Q ) % Q;
if (right - left +1 == patSize)//长度满足了,再比较内容
{
if (currentHash == patHash)//哈希值相同
{
if ( txt.substr(left,patSize) == pat)//真实比较字符串
{
return left;
}
}
currentHash = ((currentHash - txt[left] * BH) % Q + Q) % Q;// A%Q = (A+Q)%Q
left++;
}
right++;
}
return -1;
}