KMP算法
KMP:
由Knuth,Morris和Pratt发明,所以取了三位学者名字的首字母;
主要应用在字符串匹配上;
主要思想是出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,
避免从头再去做匹配了;
next:
前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串;
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串;
构造next数组其实就是计算模式串s的前缀表的过程;
前缀表记录了下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
实现 strStr()
标准的KMF算法题目。实现代码:
class Solution {
public:
void getNext(int* next, string s){
int j = 0;
next[0] = j;
for(int i = 1; i < s.size(); i++)
{
while(j > 0 && s[i] != s[j])
{
j = next[j - 1];//出现不匹配,j根据next数组中的最长相同前后缀的长度,向前跳转,
}
if(s[i] == s[j])
{
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
int next[needle.size()];
int j = 0;
getNext(next, needle);
for(int i = 0; i < haystack.size(); i++)
{
while(j > 0 && needle[j] != haystack[i])
{
j = next[j - 1];
}
if(needle[j] == haystack[i])
{
j++;
}
if(j == needle.size())//haystrack中找出needle
{
return (i - j + 1);
}
}
return -1;
}
};
我这里直接使用了前缀表作为next数组,没有做减一操作。
重复的子字符串
移动匹配法:
class Solution {
public:
bool repeatedSubstringPattern(string s) {
string t = s + s;
t.erase(t.begin());
t.erase(t.end() - 1);
if(t.find(s) != std::string::npos) return true;
return false;
}
};
KMP算法:
class Solution {
public:
void getNext(int* next, string s)
{
next[0] = 0;
int j = 0;
for(int i = 1; i < s.size(); i++)
{
while(j > 0 && s[i] != s[j])
{
j = next[j - 1];
}
if(s[i] == s[j])
{
j++;
}
next[i] = j;
}
}
bool repeatedSubstringPattern(string s) {
int len = s.size();
if(len == 0) return false;
int next[len];
getNext(next, s);
if(next[len - 1] != 0 && len%(len-next[len-1]) == 0) return true;
return false;
}
};
可以加深对next数组的理解。