kmp好理解

相比于朴素的字符串匹配算法,KMP利用了每次比较后得知的信息来减少重复比较的次数。具体利用了什么信息?这就引出了所谓的next数组。next数组,用来存放模式字符串(pattern)中每个元素的能匹配到的最长前缀。为什么需要最长前缀,当然是为了避免漏掉可能的情况,举个例子:给定pattern字符串(aaabbb),根据最长前缀的定义,可得知next数组为[0,1,2,0,0,0],给定text字符串(aaacbx),当比较到b和c时,由于它们不匹配,所以要回退模式字符串(kmp中由于利用了比较信息,所以text字符串不需要回退),此时得知的信息即前三个字符匹配,即aaa=aaa,那么模式字符串要回退多少呢,为了不漏掉每一种可能,当然是回退刚好能匹配到最长前缀的距离,即用pattern的aa匹配text的aaa,如果不是匹配最长字符串,这里就可能是用a匹配aaa,从而漏掉aa匹配aaa的情况,所以next数组里面需要保存当前位的最长前缀。得知最长前缀后,我们就可以算出pattern字符串需要回退的哪里,即最长前缀的下一位,因为最长前缀可以和text字符串匹配,所以就不需要比较了,直接比较pattern[next[current]-1](一般数组从0开始)。

所以关键就落在了求next数组上,这里贴出代码(有详细注释):

void make_next(const string& pattern, int *next)
{
	int len = pattern.length();

	int	val = 0;//用来保存next中元素的值,即最长前缀的长度
	next[0] = 0;//第一个能匹配到的最长前缀是0啊
	for (int pos = 1; pos < len; pos++)
	{
		while (val > 0 && (pattern[val - 1 + 1] != pattern[pos]))//val保存了目前能匹配到的最长前缀数,当目前匹配到的最长前缀
                                                                        //的下一位不能和pattern字符串的当前位匹配时,就需要回退val
			val = next[val-1];             //这一步是整个kmp最难的点,理解了这一步你就成功了,
		/*因为val保存的是当前可以匹配到的最长前缀,但下一个字符不能匹配最长前缀的下一个字符,所以我们要从目前最长前缀中
		找长度小于目前最长前缀的前缀来匹配,已经得知的是pos位置前面的可以和最长前缀匹配,也就是说,pos位置前的字符串就是
		目前的最长前缀,所以,我们需要找当前最长前缀的最长前缀,也就是next[val-1]保存的记录(好好举个具体的例子比划比划)
		*/
		if (pattern[val - 1 + 1] == pattern[pos])//如果目前匹配到的最长前缀的下一个字符还能和当前位置匹配,则val直接+1
			val++;

		next[pos] = val;
		
	}
}

接下来就可以根据next数组,轻松匹配字符串了,完整代码如下:

int index(const string& text, const string& pattern)
{
	int *next=new int[pattern.length()];
	int i= 0,j = 0;
	make_next(pattern, next);
	while (i < text.length() && j < pattern.length())
	{
		if ( text[i] == pattern[j])
		{
			++i; ++j;
		}
		else if (j==0&& (text[i] != pattern[j]))
		{
			i++;
		}
		else
		{
			j = next[j - 1];
		}
	}
	delete[] next;
	if (j = pattern.length())
		return i - j;
	else
		return -1;

}
int main()
{
	
	string a = "aaabbbaaabbbaaabbbc";
	string b = "aa";
	cout << index(a, b) << endl;
	
	int x;
	std::cin >> x;
}

具体更深的理论可以参阅《算法导论》(第三版)第32章。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值