KMP算法就是在一个串中找另一个串。
KMP中最重要的是next数组,它记录了最长相等前后缀。就是前缀表。
意味着在某个字符失配时,前缀表会告诉你下一步匹配中,模式串应该跳到哪个位置。
模式串与前缀表对应位置的数字表示的就是:下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
找到的不匹配的位置, 那么此时我们要看它的前一个字符的前缀表的数值是多少。
为什么要前一个字符的前缀表的数值呢,因为要找前面字符串的最长相同的前缀和后缀。
所以要看前一位的 前缀表的数值。
前一个字符的前缀表的数值是2, 所以把下标移动到下标2的位置继续比配。
next数组既可以就是前缀表,也可以是前缀表统一减一(右移一位,初始位置为-1)。
next数组中:
i:后缀
j:前缀
一般一开始把 j 设定为-1,然后其实我还有点没搞懂它的算法,大概就是如果不相等且在j大于0的情况下就回退,如果相等了就两个都加,然后把 j (因为代表的就是前缀嘛)的值赋给next[i]。
就是求next指针的算法如下:
void getNext(int* next, const string& s){
int j = -1;
next[0] = j;
for(int i = 1; i < s.size(); i++) { // 注意i从1开始
while (j >= 0 && s[i] != s[j + 1]) { // 前后缀不相同了
j = next[j]; // 向前回退
}
if (s[i] == s[j + 1]) { // 找到相同的前后缀
j++;
}
next[i] = j; // 将j(前缀的长度)赋给next[i]
}
}
然后感觉找匹配字符串的过程和求next数组的过程差不多,只是没有最后j赋值给next数组的操作。也是回退或者 j++,不同在于这个最后要加上达到匹配的条件,以及返回值。
int j = -1; // 因为next数组里记录的起始位置为-1
for (int i = 0; i < s.size(); i++) { // 注意i就从0开始
while(j >= 0 && s[i] != t[j + 1]) { // 不匹配
j = next[j]; // j 寻找之前匹配的位置
}
if (s[i] == t[j + 1]) { // 匹配,j和i同时向后移动
j++; // i的增加在for循环里
}
if (j == (t.size() - 1) ) { // 文本串s里出现了模式串t
return (i - t.size() + 1);
}
}
28.找到字符串中第一个匹配项的下标
代码就是上面那些拼接的。
class Solution {
public:
void getNext(int* next,const string str)
{
int j = -1;
next[0] = j;
for(int i=1;i<str.size();i++)
{
while(j>=0&&str[j+1]!=str[i])
{
j = next[j];
}
if(str[j+1]==str[i])
{
j++;
}
next[i] = j;
}
}
int strStr(string haystack, string needle) {
if(needle.size()==0)
return 0;
vector<int> next(needle.size());
getNext(&next[0],needle);
int j = -1;
for(int i=0;i<haystack.size();i++)
{
while(j>=0&&needle[j+1]!=haystack[i])
{
j = next[j];
}
if(needle[j+1]==haystack[i])
{
j++;
}
if(j==(needle.size()-1))
return (i-needle.size()+1);
}
return -1;
}
};
459.重复的字符串
我等会儿再写吧