❤️️💚💙💛🧡💜🖤🤍🧡
大家好!我是曾续缘🥰
欢迎关注💕
❤️点赞 👍 收藏 ⭐再看,养成习惯
🔥除了努力向前之外,还要记得时刻提醒自己,不要迷失方向,丢了你的初心。📚
Rabin-Karp算法是一种常用的字符串匹配算法,它的基本思想是将字符串转化为哈希值进行匹配,以达到快速匹配的目的。其核心思想是将字符串看作一个数字,通过对该数字进行哈希计算,判断其是否与模式串对应的哈希值相等。
以下是具体步骤:
- 定义存储结果的向量
res
。 - 获取
s
和pattern
的长度n
和m
,如果n
小于m
,则直接返回空的结果向量。 - 定义素数
P
和哈希模数MOD
。 - 计算模式串
pattern
的哈希值targetHash
和最高位权值h
。这里使用了逐个字符地计算哈希值的方法,即先初始化targetHash
和h
,然后依次加入pattern
中每个字符的贡献,其中targetHash * P + pattern[i]
表示将上一个哈希值乘以P
并加上新字符的哈希值,% MOD
表示对哈希值取模避免溢出。 - 从左到右扫描字符串
s
,依次计算每个子串的哈希值curHash
。同样地,使用逐个字符地计算哈希值的方法,每当加入一个新字符时,将当前的哈希值乘以P
并加上新字符的哈希值,再对哈希值取模。为了快速地更新哈希值,同时删除子串左侧第一个字符的贡献,需要判断当前子串的长度是否大于等于m
。如果是,将左侧第一个字符对哈希值的贡献**s[i-m] \* h**
从当前哈希值中减去即可,再对哈希值取模并保证结果非负。(**注意:**因为第一个字符的权值是最高位的权值,如果只是减去其 ASCII 码值,那相当于把第一个字符的权值忽略了,会导致计算出的哈希值偏大。因此,需要额外乘上这个权值,以保证计算出的新的哈希值是正确的。) - 如果当前子串的长度恰好为
m
,并且当前哈希值和目标哈希值相等,则需要进一步判断子串的确实内容是否与pattern
相等。如果相等,将子串在s
中的起始位置加入结果向量res
。 - 最后返回结果向量
res
。
vector<int> rabinKarp(string s, string pattern) {
vector<int> res;
int n = s.size(), m = pattern.size();
if (n < m) return res;
const long long P = 131; // 取一个素数作为哈希基数
const long long MOD = 1e9 + 7; // 取一个素数作为哈希模数
long long targetHash = 0;
long long h = 1;
for (int i = 0; i < m; i++) {
targetHash = (targetHash * P + pattern[i]) % MOD;
h = (h * P) % MOD;
}
long long curHash = 0;
for (int i = 0; i < n; i++) {
curHash = (curHash * P + s[i]) % MOD;
if (i >= m) {
curHash = curHash - (s[i - m] * h) % MOD;
if (curHash < 0) curHash += MOD;
}
if (i >= m - 1 && curHash == targetHash) {
if (s.substr(i - m + 1, m) == pattern) {
res.push_back(i - m + 1);
}
}
}
return res;
}
这里我们使用了一个素数 P 作为哈希基数,并且取了一个素数 MOD 作为哈希模数。我们首先计算出模式串的哈希值 targetHash,然后依次计算原字符串的哈希值 curHash,如果发现它与目标哈希值相等,则说明找到了一次匹配,此时可以将对应位置记录到结果数组中。为了避免哈希冲突,我们可以通过多项式哈希来计算哈希值,同时也可以通过快速幂和取模运算来提高计算效率。