实现 strStr()在给定的字符串中找到另一个字符串出现的第一个位置

实现 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’的公共前后缀,接着重复上面的过程。
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值