KMP
问题
字符串匹配问题,问字符串 str1
中是否存在连续的子串与字符串str2
相等,存在返回子串的起始位置,否则返回-1
思路
传统做法是依次遍历str1
中的每个字符作为起始位置,看是否能凑出字符串str2
.
KMP
算法就是对传统做法的一种加速,对于有些节点是可以跳过的
数据
数组next
: 用于存储下标i
前面长度为i-1
的字符串 前缀 和 后缀相等的最长长度
实现
int f1(string str1,string str2) { if(str1.size() < str2.size()) return 0; vector<int> arr = GetNextArr(str2); int i1 = 0 , i2 = 0; while(i1 < str1.size()) { if(str1[i1] == str2[i2]) { i1++; i2++; } else if(next[i2] == -1) { i1++; } else { i2 = next[i2]; } } return i2 == str2.size() ? i1 - i2 : -1; } vector<int> GetNextArr(string str) { if(str2.size() == 1) { return vector<int>(1,-1); } vector<int> next(str.size()); next[0] = -1; next[1] = 0; int cn = 0; int i = 2; while(i < str.size()) { if(next[cn] == str[i - 1]) { next[i++] = ++cn; } else if(cn > 0) { cn = next[cn]; } else { next[i++] = 0; } } return next; }
思考
如何做到加速
当i1位置字符和i2位置字符不相等时,i2来到前 i2 - 1 的 前缀和后缀相等的最长长度的下一个位置也就是next[i2].
因为i1前的字符和i2前的字符相等,可得到如图的切割,比较i1和k的字符,相当于str1的比较起始位置来到切割位置
为何可以跳过切割位置前面的字符
假设S
位置起始可以凑出字符串str2
,那S
到i1-1
的长度A
应与str2
中的长度B
中的字符相等。
由于i1
前的字符和i2
前的字符相等,则相等长度C
和A
中的字符应该相等,=》A=B=C
。
B=C
,得到新的前缀和后缀相等的最长长度,违背了我们之前算出的结果,所以S
起始不成立