28. 找出字符串中第一个匹配项的下标
题目链接:找出字符串中第一个匹配项的下标
视频讲解:帮你把KMP算法学个透!
KMP的主要思想上是当出现字符串不匹配时,可以知道一部分之前已经匹配的文本内容,可以利用这些信息避免重头再去匹配。
主要是把next数组弄清楚,通过此题初学KMP,浅浅总结一下。next数组有很多表示方式,做本题用的是原始的next数组(不做后移或者-1的操作)。那么遍历到数组的某个地方发现两元素不一样,需要做回退操作时就要找到该元素对应next数组的前一个值,才是要回退到的位置。初始化时定义的两个指针i和j,i表示指向后缀末尾的位置,j表示指向前缀末尾的位置同时它还示在i之前(包括i)最长的相等前后缀长度。
// 时间复杂度: O(n + m)
// 空间复杂度: O(m)
class Solution {
public:
void getNext(int* next, string& s) // 构建next数组
{
int j = 0; // 表示前缀尾和在i之前(包括i)最长相等前后缀长度
next[0] = 0;
for (int i =1; i < s.size(); ++i)
{
while (j > 0 && s[i] != s[j]) // 不相等的情况,j要回退,回退后不相等持续回退
{
j = next[j - 1]; // 要到j的前一个位置找next数组,确定回退的位置
}
if (s[i] == s[j]) // 相等的情况
{
j++; // j继续往前
}
next[i] = j; // 更新next
}
}
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 && needle[j] != haystack[i]) // 先判断不相等的情况,出循环可以进行相等的操作
{
j = next[j - 1];
}
if (needle[j] == haystack[i])
{
j++;
}
if (j == needle.size())
{
return (i + 1 - needle.size());
}
}
return -1;
}
};
459.重复的子字符串
题目链接:重复的子字符串
视频讲解:字符串这么玩儿,可有点儿难度!
此题在KMP的基础上想到了用字符串的长度减去最长公共前后缀的长度可以得到重复字符串的长度,只能说很难想出来,也不好理解。第一次做这题主要还是回顾KMP算法,同时了解了找重复字符串的新思路。
// 时间复杂度: O(n)
// 空间复杂度: O(n)
class Solution {
public:
void getNext(int* next, string& s)
{
int j = 0;
next[0] = 0;
for (int i = 1; i < s.size(); ++i)
{
while (j > 0 && s[j] != s[i])
{
j = next[j - 1];
}
if (s[j] == s[i])
{
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] != 0 && len % (len - next[len -1]) == 0)
{
return true;
}
return false;
}
};