题目描述
实现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1:
输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:
输入: haystack = “aaaaa”, needle = “bba”
输出: -1
说明:
当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。
对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。
思路:
这个题其实可以直接调用string的find函数
haystack.find(needle) // 直接得到答案
但是,如果调用库函数,失去了这个题的意义了,是让自己实现。
其实简单想到的方式就是暴力了,所以我的第一版代码就是暴力,就是遍历haystack,然后对于每一个字符,遍历needle,看看能不能匹配上,如果匹配完毕,直接返回位置,否则,i向后移动,然后再来。
class Solution {
public:
int strStr(string haystack, string needle) {
int len1 = haystack.size();
int len2 = needle.size();
if (len2 == 0)
return 0;
if (len1 == 0)
return -1;
int loc = -1;
for (int i=0; i<len1-len2+1; i++)
{
int j = 0;
int temp = i;
while(j<len2 && haystack[temp]==needle[j])
{ j++;
temp++;
}
if (j==len2)
{
loc = i;
break;
}
}
return loc;
}
};
但是,这个的时间复杂度有点高了,所以第二个就是BF算法了,其实也是一个暴力的方式。
class Solution
{
public:
int strStr(string haystack, string needle)
{
if(needle.empty())
return 0;
int i=0,j=0;
while(haystack[i]!='\0'&&needle[j]!='\0')
{
if(haystack[i]==needle[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(needle[j]=='\0')
return i-j;
return -1;
}
}
下面有两种改进的方式,kMP算法和Sunday算法,KMP算法是构造一个next数组,是先考虑了needle字符串本身的特定,借助每一次比较,使得不重复比较,而Sunday算法是尽量的跳过一些重复的字符。具体的没太看明白,先记录下来
// KMP
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
if (m == 0) return 0;
vector<int> ne(m + 1, -1);
for (int i = 1, j = -1; i < m; i ++)
{
while (j != -1 && needle[i] != needle[j + 1]) j = ne[j];
if (needle[i] == needle[j + 1]) j ++;
ne[i] = j;
} // 生成next数组
for (int i = 0, j = -1; i < n; i ++)
{
while (j != -1 && haystack[i] != needle[j + 1]) j = ne[j];
if (haystack[i] == needle[j + 1]) j ++;
if (j == m - 1) return i - m + 1;
}
return -1;
}
};
// Sunday算法
class Solution {
public:
int strStr(string haystack, string needle) {
if(needle.empty())
return 0;
int slen=haystack.size();
int tlen=needle.size();
int i=0,j=0;//i指向源串首位 j指向子串首位
int k;
int m=tlen;//第一次匹配时 源串中参与匹配的元素的下一位
for(;i<slen;)
{
if(haystack[i]!=needle[j])
{
for(k=tlen-1;k>=0;k--)//遍历查找此时子串与源串[i+tlen+1]相等的最右位置
{
if(needle[k]==haystack[m])
break;
}
i=m-k;//i为下一次匹配源串开始首位 Sunday算法核心:最大限度跳过相同元素
j=0;//j依然为子串首位
m=i+tlen;//m为下一次参与匹配的源串最后一位元素的下一位
if(m>slen)//当下一次参与匹配的源串字数的最后一位的下一位超过源串长度时
return -1;
}
else
{
if(j==tlen-1)//若j为子串末位 匹配成功 返回源串此时匹配首位
return i-j;
i++;
j++;
}
}
return -1;//当超过源串长度时
}
};
但是在这个题中,后面两个的速度并不是那么快,好像给出的样例没法使得后面两种算法发挥出优势。