题目来源:https://leetcode.cn/problems/find-the-index-of-the-first-occurrence-in-a-string/description/
C++题解1:使用两个for循环,对于haystack的每个字符都判断是否为匹配项,直至找到或结尾(剩余长度小于needle)
class Solution {
public:
int strStr(string haystack, string needle) {
int lenh = haystack.size();
int lenn = needle.size();
int flg = -1;
for(int ii = 0; ii < lenh - lenn + 1; ii++){
for(int jj = 0; jj < lenn; jj++){
flg = ii;
if(haystack[ii + jj] != needle[jj]) {
flg = -1;
break;
}
}
if(flg != -1) break;
}
return flg;
}
};
C++题解2:使用KMP算法,下面两个链接反复看就能看懂,模式串可能用‘aabaabaac’会好理解
思路理解:KMP 算法详解 - 知乎
代码参考:代码随想录
一点点小结:写代码的时候主要分两个内外循环。
(1)第一个内外循环是求next数组,时间复杂度是O(m);第二个内外循环是求匹配项,在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n)。
(2)两个内外循环很相似。外循环是模式串/字符串的索引更新,这个循环容易理解;内循环是匹配状态的更新,主要是在不匹配情况下,前缀表的回退状态,因为有可能一直回退,所以需要用一个while循环。
(3)注意状态跳转的jj表示跳转到模式串对应的索引上,所以第一个next[0]是-1,而“aabaabaac”的next[1]则是0,next[2]是1,依次类推,next={-1, 0, -1, 0, 1, 2, 3, 4, -1}。举例说明:假设目标字符串匹配了第一个a之后,第二个a如果匹配就继续往前走,如果不匹配就跳转到next[1](即对应索引为0),去看是否匹配第一个'a'; 假设目标字符串匹配到倒数第二个a,接下来它去匹配最后一个a,发现匹配往前走,发现不匹配,它就会跳到next[7](即索引4)的位置,然后发现跟索引next[4](即索引1)的位置也不匹配,就跳到next[1](即索引0)的位置,这样也避免了一个一个的麻烦。
class Solution {
public:
int strStr(string haystack, string needle) {
int lenh = haystack.size();
int lenn = needle.size();
if(lenh < lenn) return -1;
//求解模式串needl的next数组
int jj = -1; //jj表示状态,从-1开始
int next[lenn];
next[0] = -1;
for(int ii = 1; ii < lenn; ii++){
while(jj >= 0 && needle[ii] != needle[jj + 1]) {
jj = next[jj];
}
if(needle[ii] == needle[jj + 1]) jj++;
next[ii] = jj;
// 打印next数组
// cout<<ii<<" "<<next[ii]<<endl;
}
// 求解目标字符串的匹配索引,jj还是从-1开始
// 下面的两个循环与上面两个循环其实很相似,只是上面的外字符串为needle,下面的为haystack
jj = -1;
for(int mm = 0; mm < lenh; mm++) {
while(jj >= 0 && haystack[mm] != needle[jj + 1]) {
jj = next[jj];
}
if(haystack[mm] == needle[jj + 1]) jj++;
if(jj == lenn - 1) {
return mm - lenn + 1;
}
}
return -1;
}
};