KMP算法

136 阅读1分钟

朴素的匹配算法

简单来说就是:从主串s 和子串target 的第一个字符开始,将两字符串的字符一一比对,如果出现某个字符不匹配,主串将回溯到开始字符的下一个字符,子串回溯到第一个字符再进行一一比对。如果出现某个字符不匹配,继续上述循环,一直到子串字符全部匹配成功。这种就是我们的匹配算法,简单但是效率低。 低效的原因:回溯的步骤太多,没有充分利用已经匹配的信息。

KMP算法

实质就是解决朴素的匹配算法的低效的原因。

使用前后缀利用已经匹配的信息,如下 主串s: ababbaabc 子串target: ababc 当发现不匹配时,使子串移动最大已匹配子串的最长个相同前后缀的长度。上述例子已匹配子串为abab,它的最长个相同前后缀长度为2。

ababbaabc

---ababc

所以KMP算法最重要的部分就是获取子串的最长前后缀的长度,也就是GetNext函数。如下


void GetNext(string str, int next[])
{
	next[0] = -1;
	int len = str.size();
	int first = -1, last = 0;
	while (last < len)
	{

		if (first == -1 || str[first] == str[last])//求last+1
		{
			cout << " ss " << " ";
			first++, last++;
			//abcaabXacf									              abcaabXacf
			if (str[first] == str[last])//abcaabc(模式串)  如果不进行这步判断时,说明此时为移位如下	  abcaabc
			{							  //可知为无效移位,正确移动位abcaabXacf,即取next[next[last]],也就是next[first]的前后缀长度
																		//abcaabc
				next[last] = next[first];

			}
			else
			{
				next[last] = first;
			}
		}
		else
		{
			first = next[first];
			//我们现在知道next[first]的值代表的是下标为k的字符前面的字符串最长相等前后缀的长度
			//也表示该处字符不匹配时应该回溯到的字符的下标
			// str[first] != str[last],first应该返回next[first],如下
			//abacabaa 当first=2,last=7,first=next[2]处
		
		}
	}
}

int KPM(string give,string target)
{
	int len_g = give.size();
	int len_t = target.size();
	int* next = new int[len_t+1];
	GetNext(target, next);
	int g = 0, t = 0;

	while (g<len_g&&t<len_t)
	{
		if (t== -1 || give[g]==target[t])
		{
			g++; t++;
		}
		else
		{
			t= next[t];
		}
	}
	if (t>=len_t)
	{
		return g - t;
	}
	return -1;
}
int main()
{
    cout<< KPM("abcabadsfdfddg", "abad");
}