28. 实现 strStr()*
题目
代码
示例代码
class Solution { public: void getNext(int* next, const string& s) { int j = 0; next[0] = 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; } } int strStr(string haystack, string needle) { if (needle.size() == 0) { return 0; } int next[needle.size()]; getNext(next, needle); int j = 0; for (int i = 0; i < haystack.size(); i++) { while(j > 0 && haystack[i] != needle[j]) { j = next[j - 1]; } if (haystack[i] == needle[j]) { j++; } if (j == needle.size() ) { return (i - needle.size() + 1); } } return -1; } };
思路
本题是KMP算法的完美练习,因此要完成本题就是要理解KMP算法。
备注:个人认为对于代码随想录对KMP算法的讲解难以理解。
什么是前缀表
在暴力算法中,文本串和模式串的遍历指针遇到不匹配的情况都会回退,造成了不必要的时间浪费。
KMP算法借助部分匹配表(前缀表),使得文本串的遍历指针永不回退,遇到不匹配的情况只需要回退模式串的指针即可。
也就是说,前缀表的作用就是指导模式串的指针怎么回退
注意,计算前缀表只与模式串有关,与文本串没有任何关系。
前缀表怎么计算
前缀表的说法并不准确,这会导致我们对其理解和计算出现偏差,前缀表的“前缀”可以理解为“最长公共前后缀”,“最长相等前后缀”。
前缀表的值与前后缀都相关。
以串“aaba”为例,他的前缀是“aab”,后缀是“aba”,此时“最长公共前后缀”为1。
以串“aabaa”为例,他的前缀是“aaba”,后缀是“abaa”,此时“最长公共前后缀”为2。
请注意观察,其计算的方法比较微妙。
而前缀表就是要给出当前串的各位的“最长公共前后缀”。以“aaba”为例,它的前缀表分别要给出“a”,“aa”,“aab”,”aaba“的“最长公共前后缀”:{0,1,0,1}。
前缀表具体计算的代码形式暂且不去理解,二刷代码随想录的时候再深究。
前缀表怎么使用
前缀表是用于指导式串的指针怎么回退的,但是并不是直接使用前缀表的值进行回退,而是需要进行一些计算。
概括地来说,计算跳过长度的公式为跳过的长度=匹配上字符串中间字符长度-前缀表值,当然,可能不是很准确,二刷代码随想录的时候再深究。
459.重复的子字符串*
题目
代码
示例代码
class Solution { public: void getNext (int* next, const string& s){ next[0] = -1; int j = -1; for(int i = 1;i < s.size(); i++){ while(j >= 0 && s[i] != s[j + 1]) { j = next[j]; } if(s[i] == s[j + 1]) { j++; } next[i] = j; } } bool repeatedSubstringPattern (string s) { if (s.size() == 0) { return false; } int next[s.size()]; getNext(next, s); int len = s.size(); if (next[len - 1] != -1 && len % (len - (next[len - 1] + 1)) == 0) { return true; } return false; } };
思路
思路基本上同上。