左神讲解基础算法--kmp算法
作者:互联网
主要解决问题:包含问题。
例如:
str1: abc123def
str2:123d
str1中是否包含有str2这个字串。(注意字串与子序列区别)
子序列:可以连续也可以不连续
子数组/子串:必须是连续的。
好了废话不多说了,我们上正菜。首先,想了解kmp的加速过程,要先知道一个数组叫next数组,这个数组中存放了str2中每个字符的前缀和后缀相匹配的最长长度(注意是前缀和后缀的最长的那个匹配长度)。什么意思呢?举例说下:
图中str2中下标为0的字符它的前缀和后缀长度都是0,(记住前缀和后缀是不包含本身的这个字符的)在这里我们人为规定它的next数组值为-1,下标为1的字符的最长前缀长度是1,后缀也是1,但是我们不能包含这个前后缀字符串是同一个字符串的情况所以我们给它的next数组值为0,下标为2的字符的前缀是“a” “ab”后缀是“b”,“ab”又因为“ab”“ab”是同一字符串,所以不合格,“a”与“b”不相同,所以next[2]=0;str2[3]的前缀是“a”
“ab””aba”后缀是 “a””ba””aba”,同理”aba“不合格,所以相同字符串只有”a“ 所以 next[3]=1;
按照上面的步骤,给next数组依次赋值。
next数组知道值之后我们就要利用next数组进行加速求解字串匹配问题例如:
假设str1和str2分别从i位置和零位置一直匹配到x和y位置,(str1的长度并不是i到x,str2也不代表y就是结尾)x和y位置没有匹配上,如果是暴力解法就需要从i+1开始继续和str2[0]开始匹配,但是有了next数组我们不用这样做,因为str1和str2一直到x和y之前都相同所以x和y位置的最长前缀和最长后缀的数值是一样的所以利用y位置的next数组值找到与前缀相匹配的最长后缀位置,我们从图中str1[j]和str2[0]开始匹配,同理因为str1[j]到str2[x-1]与str2的str2[0]到str2[z-1]相同所以直接匹配str2[z]和str1[x]是否匹配。
证明:
假设 :
假设如果我们存在k位置到x位置与str2匹配,那就意味者在x位置的next数组求出来的前缀和后缀匹配的最长字符串就不是j到x-1和i到s-1了,这与我们的next数组的规则不符,所以我们直接可以证明i+1位置到j-1位置没有可能与str2匹配的子串,这就是kmp的加速过程。好了,说了这么多有的人可能听蒙了。直接上代码吧!
#include<iostream> #include<string> #include<algorithm> using namespace std; void Nextarrary(int *(&next),string *str) { if (str->length() == 1) { next[0] = -1; return; } next[0] = -1; next[1] = 0; int len = str->length(); int i = 2; int cn = 0; while(i<len) { if (str[i - 1] == str[cn]) next[i++] = ++cn; else if (cn > 0) cn = next[cn]; else next[i++] = 0; } } int GetStrmatch(string str1, string str2) { if (str1.length() == 0 || str2.length() == 0) return -1; int *next = new int[str2.length()]; int len1 = str1.length(); int len2 = str2.length(); Nextarrary(next, &str2); int i = 0; int end = 0; while (i < len1&&end < len2) { if (str1.at(i) == str2.at(end)) { end++; i++; } else if (next[end] == -1) i++; else end = next[end]; } delete[] next; next = NULL; return ((end == len2) ? i-end : -1); }
以上是代码,小编是自己学习后的总结,有不对的地方,请大佬多多指出!
标签:int,str2,str1,左神,next,后缀,算法,数组,kmp 来源: https://www.cnblogs.com/ycw1024/p/11247455.html