LeetCode28:实现strStr()

力扣28:实现strStr()
[描述]:
在模式串 pattern 中查找是否含有子串 substr,若有,返回子串第一次出现的位置,反之,返回 -1。

思考问题:若是 substr 为空时应该返回什么呢?

显然,这是一道KMP算法应用的题目。
kmp算法介绍
第一种解法:暴力解法
我们可以让字符串 pattern与字符串 substr 的所有长度为 m 的子串均匹配一次。
为了减少不必要的匹配,我们每次匹配失败即立刻停止当前子串的匹配,对下一个子串继续匹配。如果当前子串匹配成功,我们返回当前子串的开始位置即可。如果所有子串都匹配失败,则返回 −1。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;
/*
* Author: 酒馆店小二
* Description: 28.实现strStr()
* Date: 2022-02-19 13:51:56 星期六
* FileName: leetcode28.cpp
* Location: D:\VSCODE_CPP\algorithm\kmp\leetcode28.cpp
*/
int violentMatch(const string &pattern, const string &substr) {
    int pLen = pattern.size();
    int sLen = substr.size();
    int i = 0, j = 0; // i、j 分别指向 pattern 和 substr 的下标
    while (i < pLen && j < sLen) {
        if (pattern[i] == substr[j]) { // 当前字符匹配成功
            i++;
            j++;
        } else { // 当前字符匹配失败
            i = i - j + 1;
            j = 0;
        }
    }
    if (j == sLen) { // 匹配成功,返回子串在模式串中的位置,否则返回 -1
        return i - j; // 此处与kmp算法处理不同,原因详见第二种解法
    } else {
        return -1;
    }
}

int main(int argc, char* argv[]){
    string pattern = "abcdab";
    string substr = "cda";
    cout << "Match position is: " << violentMatch(pattern, substr) << endl;

    return 0;
}

在这里插入图片描述

  • 时间复杂度:O(n×m),其中 n 是字符串 pattern 的长度,m 是字符串 substr 的长度。最坏情况下我们需要将字符串 substr 与字符串 pattern 的所有长度为 m 的子串均匹配一次。
  • 空间复杂度:O(1)。我们只需要常数的空间保存若干变量。

第二种解法:kmp算法

kmp算法核心是匹配表。

vector<int> kmpNext(const string &substr) {  // KMP next 数组
    int n = substr.size();
    vector<int> next(n);
    next[0] = 0;
    for (int i = 1, j = 0; i < n; ++i) { // 注意 i 从1开始
        // 要保证 j > 0,因为会用到 j - 1 做下标
        while (j > 0 && substr[i] != substr[j]) { // 前后缀不相同
            j = next[j - 1]; // 向前回退
        }
        if (substr[i] == substr[j]) { // 找到相同的前后缀
            j++;
        }
        next[i] = j; // 将 j(前缀的长度) 赋给next[i]
    }
    return next;
}

有了匹配表,就可以使用kmp算法了。
如何判断在模式串中出现了子串?
如果子串的下标指向了子串的末尾,就说明子串完全匹配于模式串中的某个子串了。
本题要求返回模式串中子串出现的第一个字符的位置,所以返回当前在模式串匹配子串的最后一个位置 i ,再减去子串的长度,就可以得到模式串中出现子串第一个字符的位置。

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;
/*
* Author: 酒馆店小二
* Description: 28.实现strStr()
* Date: 2022-02-19 13:51:56 星期六
* FileName: leetcode28.cpp
* Location: D:\VSCODE_CPP\algorithm\kmp\leetcode28.cpp
*/
vector<int> kmpNext(const string &substr) {  // KMP next 数组
    int n = substr.size();
    vector<int> next(n);
    next[0] = 0;
    for (int i = 1, j = 0; i < n; ++i) { // 注意 i 从1开始
        // 要保证 j > 0,因为会用到 j - 1 做下标
        while (j > 0 && substr[i] != substr[j]) { // 前后缀不相同
            j = next[j - 1]; // 向前回退
        }
        if (substr[i] == substr[j]) { // 找到相同的前后缀
            j++;
        }
        next[i] = j; // 将 j(前缀的长度) 赋给next[i]
    }
    return next;
}

int kmpMatch(const string &pattern, const string &substr) {  // KMP匹配法
    // 当 substr是空字符串时,应当返回 0
    if (substr.size() == 0) {
        return 0;
    }
    int pLen = pattern.size();
    int sLen = substr.size();
    vector<int> next = kmpNext(substr);
    
    for (int i = 0, j = 0; i < pLen; i++) {
        // 仍然要保证 j > 0,因为会用到 j - 1 做下标
        while (j > 0 && pattern[i] != substr[j]) {
            j = next[j - 1]; // 注意此处,寻找上一个字符对应的匹配表的位置
        }
        if (pattern[i] == substr[j]) { // 匹配,i 和 j 同时加 1
            j++;
        }
        if (j == sLen) { // 模式串中出现了子串
           // 在第一种暴力方法中此处是 i - j
           // 二者不一样是因为第一种方法中 i 做了 ++ 操作,所以此处是 i - j + 1
            return i - j + 1;
        }
    }
    return -1;
}

int main(int argc, char* argv[]){
    string pattern = "abcdab";
    string substr = "cda";
    cout << "Match position is: " << kmpMatch(pattern, substr) << endl;

    return 0;
}

修改于:2022.02.19
给岁月以文明,给时光以生命

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值