学习笔记—KMP算法

1、某个位置前面最长前缀和最长后缀的匹配长度:

限定:前缀不能包含最后一个字符,后缀不能包含第一个字符。

字符串 "abcabcd" 的最长前缀和最长后缀匹配的长度为3。当前缀和后缀取1的时候,前缀为a,后缀为c,不匹配;当前缀和后缀取2的时候,前缀ab,后缀bc,不匹配;前缀和后缀取3,前缀abc,后缀abc,匹配,那它是否最长的匹配长度? 继续看,前缀和后缀为4,前缀 abca,cabc,不匹配;取5,前缀abcab,后缀 bcabc,不匹配;取6,前缀abcabc最后一个c取到后缀,超出限定,舍弃,停止,所以最长前缀和最长后缀的最长匹配就是3,匹配字符为abc.

学习笔记—KMP算法

2、next数组。next数组记录的就是字符串当前某个字符前面的匹配信息,所以next数组和该字符串长度相等。比如遍历字符串 "abcabcd",字符串第一个位置a,作为第一个字符,a前面没有所谓的前缀和后缀,规定其位置在next数组的元素为-1;第二个位置b,其前面只有一个字符,而限定中说到前缀不能取到后缀,所以规定其在next数组的位置为0;第三个位置其前缀为a,后缀为b,不匹配,为0;第四个位置a,,没有匹配的前后缀,为0;第五个位置b,匹配的只有前缀a和后缀a;第六个位置c,匹配的是前缀ab和后缀ab,所以为2,第7个位置匹配的是abc,为3。所以该字符串的next数组为【-1 0 0 0 1 2 3】

实现思路:

学习笔记—KMP算法

 

代码实现:

#include<iostream>
#include<string>
using namespace std;
int* getNextArray(string str2) {
	if (str2.length() == 1) {
		int* next = new int[1];
		next[0] = -1;
		return next;
	}
	int* next = new int[str2.length()];
	next[0] = -1;
	next[1] = 0;
	int i = 2;
	int cn = 0;//当i=2的时候,下面比较的时候是1位置与0位置是否相等,所以cn从0开始
	while (i < str2.length()) {
		if (str2[i - 1] == str2[cn]) { //考虑i-1位置与cn位置是否相等,如果相等则当前位置的next数组元素为cn+1
			next[i++] = ++cn;
		}
		else if (cn > 0) {//还可以往前跳,cn会跳到当前当前cn的next数组元素位置,完成cn的更新
			cn = next[cn];
		}
		else {
			next[i++] = 0;//无法跳了。
		}
	}
	//退出循环意味着数组已经填完了,
	return next;
}

3、匹配过程。

学习笔记—KMP算法

目标:在str1中找是否存在str2子串

第一步:i1指向str1首字符,i2指向str2首字符,如果i1字符和i2字符相等,则i1++,i2++

第二步:不等,没配上,则判断i2的next数组元素是否为-1,如果是则i2处于首字符位置,此时只需要i1++

第三步:如果i2的next数组元素不为-1,不是首字符位置,则i2位置应该跳当前next数组位置再往下匹配

第四步:如果i2遍历完了str2说明匹配上了否则返回-1

代码:

int getIndexOf(string str1, string str2) {
	if (str1.length() == 0 || str2.length() == 0)
		return -1;
	int i1 = 0;//str1
	int i2 = 0;//str2
	int* next = getNextArray(str2);//求解next数组
	while (i1 < str1.length() && i2 < str2.length()) {
		if (str1[i1] == str2[i2]) {
			i1++;
			i2++;
		}
		else {//没配上,往前跳
			//如果当前i2位置的next数组位置值为-1表明i2在第一个位置,没得跳,第一个位置都没配上,i1++
			if (next[i2] == -1) {
				i1++;
			}
			else {
				i2 = next[i1];//跳到next数组中那个位置
			}
		}
	}
	return i2 == str2.length() ? i1 - i2 : -1;
}

 完整测试代码:

#include<iostream>
#include<string>
using namespace std;
int* getNextArray(string str2) {
	if (str2.length() == 1) {
		int* next = new int[1];
		next[0] = -1;
		return next;
	}
	int* next = new int[str2.length()];
	next[0] = -1;
	next[1] = 0;
	int i = 2;
	int cn = 0;//当i=2的时候,下面比较的时候是1位置与0位置是否相等,所以cn从0开始
	while (i < str2.length()) {
		if (str2[i - 1] == str2[cn]) { //考虑i-1位置与cn位置是否相等,如果相等则当前位置的next数组元素为cn+1
			next[i++] = ++cn;
		}
		else if (cn > 0) {//还可以往前跳,cn会跳到当前当前cn的next数组元素位置,完成cn的更新
			cn = next[cn];
		}
		else {
			next[i++] = 0;//无法跳了。
		}
	}
	//退出循环意味着数组已经填完了,
	return next;
}
int getIndexOf(string str1, string str2) {
	if (str1.length() == 0 || str2.length() == 0)
		return -1;
	int i1 = 0;//str1
	int i2 = 0;//str2
	int* next = getNextArray(str2);//求解next数组
	while (i1 < str1.length() && i2 < str2.length()) {
		if (str1[i1] == str2[i2]) {
			i1++;
			i2++;
		}
		else {//没配上,往前跳
			//如果当前i2位置的next数组位置值为-1表明i2在第一个位置,没得跳,第一个位置都没配上,i1++
			if (next[i2] == -1) {
				i1++;
			}
			else {
				i2 = next[i2];//跳到next数组中那个位置
			}
		}
	}
	return i2 == str2.length() ? i1 - i2 : -1;
}

int main() {
	string s1, s2;
	while (cin >> s1 >> s2) {
		cout << getIndexOf(s1, s2);
	}
	return 0;
}

学习笔记—KMP算法

 

 

上一篇:luoguP3979 遥远的国度


下一篇:同余最短路