字符串hash算法

1、原理

1.1 前缀+首部指数最小形式

字符串s的哈希定义为

hash(x)=(\sum_{i=0}^{l-1}s[i] * p^{i}) \mod m

子串s[l..r]的哈希hash(s[l..r])=(\sum_{i=l}^{r}s[i]*p^{i-l}) \mod m,有

\begin{align*} hash(s[l..r]) * p^{l} &=(\sum_{i=l}^{r}s[i]*p^{i}) \mod m \\ &= (\sum_{i=0}^r s[i] * p^i - \sum_{i= 0}^{l - 1} s[i] * p^i) \bmod m \\ &= \left( hash(r) - hash(l - 1)\right ) \bmod m \end{align*}

递推关系式为:hash(i)=hash(i-1)+s[i]*p^{i}

1.2 前缀+首部指数最高形式

另外一种表示形式为

hash(x)=\sum_{i=0}^{l-1}s[i]*p^{l-1-i} \bmod m

递推关系有:

 hash(i) = hash(i-1) * p + s[i]

子串s[l..r]的哈希为:

\begin{align*} hash(s[l..r]) &= \sum_{i=l}^r s[i] * p^{r - i} \bmod m \\ &= \sum_{i=0}^r s[i] * p^{r - i} \bmod m - \sum_{i=0}^{l-1}s[i] * p^{r-i} \bmod m \\ &=\sum_{i=0}^r s[i] * p^{r - i} \bmod m - \sum_{i=0}^{l-1}s[i] * p^{l - 1 - i} * p^{r-l + 1} \bmod m \\ &=hash(s[0..r]) - hash(s[0..l-1]) * p^{r-l+1} \end{align*}

上面的都是基于前缀来计算哈希值。

1.3 后缀+首部指数最小形式

另外一种形式是基于后缀来计算哈希值。满足递推式:

hash(i) = hash(i+1)*p + s[i],其中0 \le i \textless n, hash(n) = 0

s[i..n-1]的哈希值hash(i) = \sum_{k=i}^{n-1} s[k] * p^{k - i},对于长度为L的子串s[i..i+L-1],其哈希值为

\begin{align*} hash(s[i..i+L-1]) &= \sum_{k=i}^{i+L-1} s[k] * p^{k - i} \\ &= \sum_{k=i}^{n-1} s[k] * p^{k-i} - \sum_{k=i+L}^{n-1} s[k] * p^{k - i} \\ &= \sum_{k=i}^{n-1} s[k] * p^{k-i} - \left(\sum_{k = i+L}^{n-1} s[k] * p^{k - i - L} \right ) * p^L \\ &= hash(i) - hash(i+L) * p^L \end{align*}

2、rabin-karp算法

问题:给出两个字符串,模式s和文本t,确定模式s是否在文本t中出现,如果是,则列举出所有出现的。

算法 :

  1. 计算模式s的哈希
  2. 计算文本t的所有前缀的哈希值。
  3. 使用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

126B Password codeforces

substring problem SPOJ 超时

INSQ15_A codechef

Ada and Spring Cleaning spoj

Text Editor codeforces

12012 Detection of Extraterrestrial UVa

E Games on a CD codeforces

11855 Buzzlewords UVa

D Santa Claus and a Palindrome codeforces

F String Compression codeforces

D Palindromic Characteristics

CF25E Test spoj

D Palindrome Degree codeforces

C Deletion of Repeats codeforces 

Gift Boxes HackerRank

Pattern Find spoj(rabin-karp)

D Good Substrings codeforces

参考资料:

挑战程序设计竞赛(第2版)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kgduu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值