字符串匹配之RK算法——学习笔记

RK算法是Rabin-Karp算法的简称,是经典的字符串匹配算法,在《算法导论》上是有介绍的,有兴趣的同学可以去看看。

RK算法的复杂度可以说是比上不足比下有余,比一般的匹配算法要好,但是又比不上KMP,Sunday等算法。算法表现跟快排比较相似,算法平均复杂度表现较好,但最坏情况时复杂度会相对较高。

 

RK算法的核心思想类似于hash函数。对于hash函数有了解的同学应该知道,我们通过hash函数可以将一个字符串映射成一个数字(hash值)。当两个字符串的hash值不相同时,说明这两个字符串一定不匹配,而相同时则说明两个字符串是有可能匹配的,那么就需要进一步验证。

 

首先给两个字符串,假设str长度为n,pattern长度为m:

char str[]={"abcdefgdcbaefg"};
char pattern[]={"gdcbae"};

我们利用下面的公式可以用pattern计算出一个值X:

d=26;
L=P.length;
X=P[L-1]+d*(P[L-2]+d*(P[L-3]+...+(P[1]+d*P[0])));

把pattern带入上面P中,可以求出一个值pattern_x,这个值就是代表pattern的hash值,或者说是映射值。d的取值根据字符串字符的所有可能性值的数量来分配。比如数字0~9,此时d=10,又如字母a~z,此时d=26。

我们可以在str中找到 (n-m+1) 个长度与pattern相同的子串str_sub={str[i],,,str[i+m-1]},如下:

char str[]={"abcdefgdcbaefg"};

char st1[]={"abcdef"};
char st2[]={ "bcdefg"};
...
char st(n-m+1)[]={  "cbaefg"};

然后利用上面的公式就计算出这些子串的映射值,与pattern的映射值进行比较后,排除与pattern_x 不相同的映射值,留下相同的值,再取出子串和pattern之间进行字符比较,判断是否匹配。

还有一个小问题,就是当pattern较长的时候,pattern_x会比较大,可能会超过最大长度范围。这时只要取一个素数q,计算的时候对映射值取余即可解决这个问题。q取素数会比较好,一般满足q*d

 

好了,来看代码吧

//验证两个字符串是否相同
bool isMatching(char *str,char *pattern) {
	for (int i = 0; pattern[i] != '\0'; i++) {
		if (pattern[i] != str[i]) {
			return false;
		}
	}
	return true;
}

//主函数
bool Fun(char *str, char *pattern) {
	int size1 = strlen(str);
	int size2 = strlen(pattern);

	int d = 26;
	int q = 144451;

	//str子串的映射值
	int s_code = str[0]-'a';    
	//pattern的映射值
	int p_code = pattern[0]-'a';
	//h为d的size2-1次幂
	int h=1;

	//计算 s_code、p_code、h
	for (int i = 1; i < size2; i++) {
		p_code = (d*p_code + pattern[i]-'a') % q;
		s_code = (d*s_code + str[i]-'a') % q;
		h = (h*d)%q;
	}

	//字符串开始匹配,对p_code和s_code 进行比较,并更新s_code的值
	for (int i = 0; i < size1 - size2+1; i++) {
		if (s_code == p_code&&isMatching(str+i,pattern)) {
			return true;
		}

		//更新s_code,去掉开头的值str[i],加上str[i+size2]
		s_code = ((s_code - h * (str[i] - 'a'))*d + str[i + size2] - 'a') % q;
	}
	return false;
}

更新str子串映射值可能会稍微难理解一些,举个例子:

char str[]={"abcdefgdcbaefg"};
char st1[]={"abcdef"};
char st2[]={ "bcdefg"};

st1和st2中间的部分一致,仅首尾变化了,那么可以推出:

st1_x-st1[0]*pow(d,m-1)%q=(st2_x-st2[m-1])/d;
st2_x=((st1_x-st1[0]*pow(d,m-1)%q)*d+st2[m-1])%q;

st1_x,st2_x为映射值。也就有了我上面代码中的公式:

s_code = ((s_code - h * (str[i] - 'a'))*d + str[i + size2] - 'a') % q;

好了,不早了,休息去了~~~~

你可能感兴趣的:(基础算法学习)