LeetCode28-实现strStr()
实现 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() 定义相符。
一、思路
(一)字符串匹配
每次匹配从第一个字符开始,若匹配上了,则继续匹配直到某一次匹配不成功或者全部都能匹配上。
C++代码:
class Solution {
public:
int strStr(string haystack, string needle) {
if (needle.empty())
return 0;
if (needle.size() > haystack.size())
return -1;
for (int i = 0; i < haystack.size(); i++) {
if (haystack[i] == needle[0]) {
int j = 1;
while (j < needle.size() && (i + j) < haystack.size() && haystack[i + j] == needle[j])
j++;
if (j == needle.size())
return i;
}
}
return -1;
}
};
执行效率:
(二)KMP算法
详细的讲解可以去学堂在线看看清华的数据结构课程,这里只是简要说明一下:
假设待匹配的字符串为T,模式串为P,需要在字符串T中找出P子串,设P的长度为n,T的长度为m
在某次匹配中,P[i]与T[j]不匹配,这就意味着:
之前的匹配都是成立的,即:子串P[0,i)=T[j-i,j)
考虑那个接替字符P[i]与T[j]进行匹配的字符P[k]需要什么条件:
如果将P[k]放置在P[i]的位置上,那就意味着k之前的位置都是匹配的,即:
(1)P[0,k)=T[j-k,j)
又因为:P[0,i)=T[j-i,j)并且k<i,所以有:T[j-k,j)=P[i-k,i),即:
(2)P[0,k)=P[i-k,i)
此时,移动的距离为:i-k
为了不漏掉某个子串,移动距离应该尽可能的小,因此k要尽量大些
看到这里你会发现:
每次接替不匹配字符P[i]的P[k],这里的k和字符串T是没有任何关系的,它只与模式串P有关
因此,我们可以事先构建好一张表next,这样每当发生不匹配的时候,我们都可以直接找到下一个最合适的接替字符
如果在一开始的时候,就发生了不匹配,即:P[0]!=T[j],这时已经没有可以接替P[0]的字符,next表中该填什么?
这时,我们可以假设P[0]前面还有一个通配符,它与任意字符都是匹配的,即:P[-1]=*
如何生成next表?我们可以先看看next[i]与next[i+1]的关系:
从next表的含义出发:
next[i]是接替字符P[i]的字符在P中的位置,即:用P[next[i]]来代替P[i]
那么对于next[i]来说,必然有:
P[0,next[i])=P[i-next[i],i)
如果:P[next[i]]=P[i],那么对于P[i]来说,就是一次无用的匹配,但是从next[i+1]的角度来看:
P[0,next[i])=P[i-next[i],i)可以更进一步:P[0,next[i]+1)=P[i-next[i],i+1)
对比next[i+1]的意义:P[0,next[i+1])=P[i+1-next[i+1],i+1)
这里替换一下,令:next[i+1]=next[i]+1
P[0,next[i]+1)=P[i-next[i],i+1)
之前说过,next[i]应该要尽可能的大,因为这个next[i]说的是从0~(i-1)找出能接替P[i]的字符位置,所以next[i]<i
根据之前的分析:next[i+1]的最大长度也只能是next[i]+1,因此next[i+1]=next[i]+1<i+1
如果P[next[i]]!=P[i],这是不匹配的状况
分析一下,P[next[i]]!=P[i]这个不等式隐含的条件是:
P[0,next[i])=P[i-next[i],i)
也就是说前半部分相等,这个时候应该考虑找个字符串来接替P[next[i]]了,根据next的意义,我们知道:
设k=next[i],接替P[k]的是P[next[k]],然后再使用P[next[k]]]与P[i]进行比较,如此反复,直到相等
生成next表的C++代码:
vector<int> KMP(string s) {
vector<int> next;
next.push_back(-1);
int k = -1;
int j = 1;
while (j < s.size()) {
if (k == -1 || s[j - 1] == s[k]) {
next.push_back(++k);
j++;
}
else
k = next[k];
}
return next;
}
C++代码:
class Solution {
public:
int strStr(string haystack, string needle) {
if (needle.empty())
return 0;
if (needle.size() > haystack.size())
return -1;
vector<int> next = KMP(needle);
int i = 0, j = 0;
while (i < haystack.size() && abs(j) < needle.size()) {
if (j == -1 || haystack[i] == needle[j]) {
i++;
j++;
if (j == needle.size())
return (i - j);
}
else {
j = next[j];
}
}
return -1;
}
vector<int> KMP(string s) {
vector<int> next;
next.push_back(-1);
int k = -1;
int j = 1;
while (j < s.size()) {
if (k == -1 || s[j - 1] == s[k]) {
next.push_back(++k);
j++;
}
else
k = next[k];
}
return next;
}
};
执行效率: