【题目描述】
给你两个字符串 haystack
和 needle
,请你在 haystack
字符串中找出 needle
字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle
不是 haystack
的一部分,则返回 -1
。
【测试用例】
示例1:
输入:haystack = "sadbutsad",needle = "sad"
输出:0
解释:"sad"在下标0和6处匹配。第一个匹配项的下标是0,所以返回0。
示例2:
输入:haystack = "leetcode",needle = "leeto"
输出:-1
解释:"leeto"没有在"leetcode"中出现,所以返回-1。
【思路分析】
这道题是经典的字符串匹配问题:即在主串寻找子串,如果存在子串则返回子串在主串中的起始位置,如果不存在则直接返回-1。给出两种解法。
法一:简单暴力法
这种方法是大部分人第一时间都能想到的方法。简单直接,索引 i 用于遍历主串haystack,索引 j 用于遍历子串needle,均从串起始位置0开始:
如果haystack[i] == needle[j],则两个索引都后移继续比较下一个位置;
如果haystack[i] != needle[j],则 i = i - j + 1(主串指针回退到本轮匹配起始位置的下一个位置),j = 0(子串指针回退到起始位置)。
循环结束后,如果子串指针 j 与子串长度相等,说明匹配成功(因为j就表示子串中前j个元素匹配成功),返回 i - j ,即为子串在主串中的起始位置。
否则直接返回-1。
法二:KMP算法
KMP算法是字符串匹配这一问题中的神级算法,据我所知至今没人能对这个算法再进行改进。所以它的代码也很固定,记住就行(反正我是记不住,考研的时候学了几遍都理解不了它的代码为什么可以这么简洁!),不过理解KMP的原理才是最重要的,因为它的代码固定,所以不记得代码的时候网上一搜就可以,原理才是根本。
KMP和法一的本质区别就在于在 haystack[i] != needle[j] 时,i 不用回退,而 j 也不用每次都回退到0,具体退到何处由next数组确定,next数组是KMP的核心。
具体的KMP原理读者可以自行搜索查阅,如果写在这里篇幅太大(其实是本人水平有限,不一定能给你讲懂哈哈哈)。
ps:KMP代码的优越性体现在主串子串很长的情况下,像这一类题目的测试用例我想应该都不会很长,所以在LeetCode提交后这两种方法的差距体现不出来。如图所示:
法一C实现:
法一C++实现:
法二C实现:
【参考代码】
法一:C实现
#include <stdio.h>
#include <string.h>
//easy-28-找出字符串中第一个匹配项的下标
int strStr(char* haystack, char* needle);
int main(){
char *haystack, *needle;
scanf("%s",haystack);
scanf("%s",needle);
int res = strStr(haystack, needle);
printf("%d\n",res);
return 0;
}
int strStr(char* haystack, char* needle){
int i=0,j=0;
while(i<strlen(haystack) && j<strlen(needle)){
if(haystack[i] == needle[j]){
i++;
j++;
}else{
i = i - j + 1;
j = 0;
}
}
if(j == strlen(needle)){
return i-j;
}
return -1;
}
法一:C++实现
#include <iostream>
#include <string>
using namespace std;
//easy-28-找出字符串中第一个匹配项的下标
class Solution {
public:
int strStr(string haystack, string needle);
};
int Solution::strStr(string haystack, string needle){
int i=0, j=0;
while(i<haystack.size() && j<needle.size()){
if(haystack[i] == needle[j]){
i++;
j++;
}else{
i = i - j + 1;
j = 0;
}
}
if(j == needle.size()){
return i-j;
}
return -1;
}
int main(){
string haystack, needle;
cin>>haystack;
cin>>needle;
Solution sol;
int res = sol.strStr(haystack, needle);
cout<<res<<endl;af
return 0;
}
法二:C实现
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//easy-28-找出字符串中第一个匹配项的下标
int strStr(char* haystack, char* needle);
int main(){
char *haystack, *needle;
scanf("%s",haystack);
scanf("%s",needle);
int res = strStr(haystack, needle);
printf("%d\n",res);
return 0;
}
int strStr(char* haystack, char* needle){
int i=0, j=-1;
int *next = (int*)malloc(sizeof(int)*(strlen(needle)+1));
next[0] = -1;
//求next数组
while(i<strlen(needle)){
if(j==-1 || needle[i]==needle[j]){
++i;
++j;
next[i] = j;
}else{
j = next[j];
}
}
//KMP主体
i=0;
j=0;
int len1 = strlen(haystack);
int len2 = strlen(needle);
while((i<len1) && (j<len2)){
if(j==-1 || haystack[i]==needle[j]){
++i;
++j;
}else{
j = next[j];
}
}
if(j == strlen(needle)){
return i-strlen(needle);
}
return -1;
}