题目
实现 strStr() 函数。
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串出现的第一个位置(下标从 0 开始)。如果不存在,则返回 -1 。
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与 C 语言的 strstr() 以及 Java 的 indexOf() 定义相符。
示例 1:
输入:haystack = “hello”, needle = “ll”
输出:2
示例 2:
输入:haystack = “aaaaa”, needle = “bba”
输出:-1
示例 3:
输入:haystack = “”, needle = “”
输出:0
- 方法一:暴力匹配
class Solution {
public:
int strStr(string haystack, string needle) {
//如果模式串为空,返回下标0
if(needle.empty()) return 0;
int n = haystack.size() - needle.size() + 1;
//暴力匹配
for(int i=0; i<n; i++){
int j = i;
int k = 0;
while(j<haystack.size()&&k<needle.size()&&haystack[j]==needle[k]){
j++;
k++;
}
if(k == needle.size()) return i;
}
return -1;
}
};
-
空间复杂度O(1)
-
时间复杂度O(n*m)
-
思路:对于文本串的每一个字符,在它到末尾的长度大于等于模式串时,均进行一次匹配,成功则返回下标,失败则进行下一次匹配
-
方法二:KMP
class Solution {
public:
//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[j+1]==s[i]){
j++;
}
//记录i处的最长匹配前缀的长度-1
next[i] = j;
}
}
//KMP算法实现串匹配
int strStr(string haystack, string needle) {
if(needle.empty()) return 0;//模式串为空返回0
int next[needle.size()];//next数组
getNext(next,needle);//构造next数组
int j = -1;//模式串指针
for(int i=0; i<haystack.size(); i++){
//模式串与文本串在当前位置不匹配时
while(j>=0 && haystack[i]!=needle[j+1]){
j = next[j];
}
//模式串与文本串在当前位置匹配时
if(haystack[i]==needle[j+1]){
j++;
}
//匹配成功返回文本串第一个匹配位置的下标
if(j == (needle.size()-1)){
return (i-needle.size()+1);
}
}
return -1;
}
};
-
时间复杂度O(n+m)
-
空间复杂度O(m)
-
思路
- KMP的思想是当出现字符不匹配时,利用已经匹配的一部分文本内容,避免下次匹配从头开始
- next数组(next[0]为-1,即-1版)
- next数组是一个前缀表,它记录了文本串与模式串不匹配时,模式串应该从哪里开始重新匹配。
- 前缀表:记录下标i之前(包括下标i)的字符串中,有多大长度相同前缀后缀。当前位置匹配失败后,找到之前已经匹配上的前缀,再重新匹配。
- 前缀:不包含最后一个字符的所有以第一个字符开头的连续子串
- 后缀:不包含第一个字符的所有以第一个字符结尾的连续子串
- next数组的计算:j指向前缀终止的位置(减1版中是终止位置减1的位置),i指向后缀终止位置(减1版中是终止位置减1的位置)。next[i]表示i之前最长相等的前缀后缀长度(j),初始化next[0]为j。前后缀末尾不匹配时j要向前回溯,到next[j],继续比较。如果前后缀末尾匹配,j后移,将j的值赋值给next[i]。
- 文本串与模式串匹配
- 对于文本串的每个字符,逐一与模式串相匹配。当前文本串的字符匹配失败,匹配下一个字符时,模式串中匹配开始的位置为与该字符的后缀相同的前缀的下一个位置。
- 求next数组的值实际上就是模式串与自身做串匹配。
-
next数组中的值为前缀和
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;
}
};