1、原理
1.1 前缀+首部指数最小形式
字符串s的哈希定义为
子串s[l..r]的哈希,有
递推关系式为:
1.2 前缀+首部指数最高形式
另外一种表示形式为
递推关系有:
子串s[l..r]的哈希为:
上面的都是基于前缀来计算哈希值。
1.3 后缀+首部指数最小形式
另外一种形式是基于后缀来计算哈希值。满足递推式:
,其中
s[i..n-1]的哈希值,对于长度为L的子串s[i..i+L-1],其哈希值为
2、rabin-karp算法
问题:给出两个字符串,模式s和文本t,确定模式s是否在文本t中出现,如果是,则列举出所有出现的。
算法 :
- 计算模式s的哈希
- 计算文本t的所有前缀的哈希值。
- 使用2中计算出的哈希值来计算长度为 |s|的子串的哈希与模式的哈希作比较。
其时间复杂度为O(|s|+|t|),其中O(|s|)表示计算模式s的哈希,O(|t|)表示计算文本t的所有前缀的哈希值以及长度为|s|的子串与模式的哈希作比较所有的时间
改善发生冲突的方式可以计算两个哈希函数,要么使用两个不同的p或者两个不同的m,比较这两个哈希对来看两个字符串是否相同
typedef long long ull;
const int R = 131;
const ull MOD = 1e9 + 7;
#define REP(i, a, b) for (int i = (a); i < (b); ++i)
class RabinKarp
{
public:
RabinKarp(const string& pattern) : pat(pattern)
{
patLen = pat.size();
rm = 1;
REP(i, 0, patLen - 1) {
rm = rm * R % MOD;
}
patHash = calHash(pat, patLen);
}
bool search(const string& text)
{
int textLen = text.size();
ull textHash = calHash(text, patLen);
if (textHash == patHash && check(text, 0)) {
return true;
}
REP(i, patLen, textLen) {
textHash = (textHash - rm * text[i - patLen] % MOD + MOD) % MOD;
textHash = (textHash * R + text[i]) % MOD;
if (textHash == patHash && check(text, i - patLen + 1)) {
return true;
}
}
return false;
}
private:
ull calHash(const string& s, int len)
{
ull h = 0;
REP(i, 0, len) {
h = (h * R + s[i]) % MOD;
}
return h;
}
bool check(const string& txt, int i)
{
/*
REP(j, 0, patLen) {
if (pat[j] != txt[i + j]) {
return false;
}
}
*/
return true;
}
private:
const string& pat;
int patLen;
ull patHash;
ull rm;
};
3、应用
3.1 子串是否存在
使用64位无符号整数来计算哈希值。
using ull = unsigned long long;
const ull B = 131;
bool contain(const string& a, const string& b)
{
int a1 = a.length(), b1 = b.length();
if (a1 > b1) {
return false;
}
ull t = 1;
for (int i = 0; i < a1; i++) {
t *= B;
}
ull ah = 0, bh = 0;
for (int i = 0; i < a1; i++) {
ah = ah * B + a[i];
bh = bh * B + b[i];
}
for (int i = 0; i + a1 <= b1; i++) {
if (ah == bh) {
return true;
}
if (i + a1 < b1) {
bh = bh * B + b[i + a1] - b[i] * t;
}
}
return false;
}
3.2 a前缀和b后缀相等的最大长度
using ull = unsigned long long;
const ull B = 131;
int overlap(const string& a, const string& b)
{
ull t = 1, ah = 0, bh = 0;
int ans = 0;
int a1 = a.length(), b1 = b.length();
for (int i = 1; i <= min(a1, b1); i++) {
ah = ah + a[a1 - i] * t;
bh = bh * B + b[i - 1];
if (ah == bh) {
ans = i;
}
t *= B;
}
return ans;
}
实践
A Needle in the Haystack - SPOJ
154C DoubleProfiles codeforces
12012 Detection of Extraterrestrial UVa
D Santa Claus and a Palindrome codeforces
F String Compression codeforces
D Palindrome Degree codeforces
C Deletion of Repeats codeforces
Pattern Find spoj(rabin-karp)
参考资料:
挑战程序设计竞赛(第2版)