28 实现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

  • 方法一:暴力匹配
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;
    }
};

KMP参考详解:代码随想录

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值