实现 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
作者:力扣 (LeetCode)
链接:https://leetcode.cn/leetbook/read/array-and-string/cm5e2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1.本题可以暴力破解
写的有点儿啰嗦,看别人写的比这个简洁很多。
int strStr(char * haystack, char * needle){
int idx = -1;
int flag = 0;
if (*needle == '\0')
{
return 0;
}
while(*haystack != '\0')
{
char* needle_tmp = needle;
char* haystack_tmp = haystack;
while(*needle_tmp != '\0')
{
flag = 1;
if (*needle_tmp == *haystack_tmp)
{
needle_tmp++;
haystack_tmp++;
}
else
{
flag = 0;
break;
}
}
idx++;
haystack++;
if (flag == 1)
{
break;
}
}
if (flag == 0)
{
idx = -1;
}
return idx;
}
别人的解法,简练很多
int BF(char S[],char T[])
{
int i=0,j=0;
while(S[i]!='\0'&&T[j]!='\0')
{
if(S[i]==T[j])
{
i++;
j++;
}
else
{
i=i-j+1;
j=0;
}
}
if(T[j]=='\0') return (i-j); //主串中存在该模式返回下标号
else return -1; //主串中不存在该模式
}
————————————————
版权声明:本文为CSDN博主「yyzsir」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yyzsir/article/details/89462339
第二种解法,kmp解法。这道题主要是想考kmp。下面这篇文章写的很好,在此引用一下。
查看原文
https://blog.csdn.net/yyzsir/article/details/89462339
class Solution {
public:
vector<int> next;
void buildNext(string s) {
int m = s.size(), j = 0;
int t = -1;
next.resize(m);
// 因为第一个字母没有前缀,所以next[0] = -1
next[0] = t;
while (j < m - 1) {
// t < 0 也就是 t == -1,要从模式串的第一位开始匹配,然后主串也要向后移一下
if (t < 0 || s[j] == s[t]) {
j++;
t++;
next[j] = t;
} else {
t = next[t];
}
}
}
int strStr(string haystack, string needle) {
if (needle.size() == 0) return 0;
buildNext(needle);
// 指向主串
int n = haystack.size(), i = 0;
// 指向模式串
int m = needle.size(), j = 0;
while (i < n && j < m) {
// j < 0 说明 j==-1要从头开始匹配了
if (j < 0 || haystack[i] == needle[j]) {
i++;
j++;
} else {
// haystack[i] 和 needle[j]不匹配,要从模式串下标为next[j]的继续匹配,也就是最长公共前缀后缀的长度
j = next[j];
}
}
// 如果j == m证明模式串匹配完毕,在主串中找到了模式串,范围模式串在主串中出现的第一个下标,i - j
if (j == m) {
return i - j;
}
return -1;
}
};
详细算法说明,看原文即可。在此记录一下疑惑过的点。
1.在算法中提到如果s[i] !=t[j]时,令j = next[j],重新从s[i]和t[j]进行比较,为什么可以一下跳过很多个字符进行比较呢,在文章中提到正面的解释,已经很清晰了。就是假如next[j] = 1,那说明有一个最长公共前后缀,所以有j-1前面那个字符就和t的第一个字符是相同的,也就不用再比较了,因为刚刚已经比较过了,它两是相同的。如下图所示,这个比较好理解。
那么为什么t可以一下向右移动3个呢,中间的两次为什么就不比较了呢?
这就是kmp算法的精华了吧,因为在算next数组的时候已经算过了,c前面只有长度为1的最长公共前后缀。也就是说长度为2的后缀ca和前缀ab是不相等的,长度为3的后缀bca和前缀abc是不想等的。而这两个后缀又分别和s中对应位置的字符串是相等的,那就可以推出来长度为2、3的前缀abc和s中的bca不相等,ab和s中的ca不相等,那就没有必要进行比较了。
所以可以跳过中间右移1,右移2这两次比较。直接从右移3开始比较,并且由于第一个字符已经知道相等了,也不用比较了。
想要实现kmp算法,求next数组是个关键,感觉代码的难点反而在这里。
这张图画出了精髓,先判断Pk和Pj,如果相等就next[j+1] = next[j]+1 = k + 1; 如果不相等就看看前面k个字符串中有没有P0~~Pn和Pm ~Pj相等,怎么判断呢,由于是公共前后最,两个k下面的字符串是相同的,那么就用前面那个来处理吧。在这k个字符中也有他自己的公共前后缀,并且已经记录到了next[]数组中了,就是next[k]。所以让K’ = next[k],然后比较一下Pk‘ 是不是和Pj相等,如果相等,那这就是Pj+1的最长公共前后缀了(因为Pj前面的k个字符和Pk前面的是相同的,Pk的前面k‘个字符和Pj前面的k’个字符是相同的,所以P0 ~Pk‘ = Pj-k’ ~ Pj,所以k’+1就是最长前后缀的长度了)。如果不相等那就接着找Pk’的公共前后缀,接着重复上面的过程。