【算法与数据结构】28、LeetCode实现strStr函数

这篇具有很好参考价值的文章主要介绍了【算法与数据结构】28、LeetCode实现strStr函数。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。

一、题目

【算法与数据结构】28、LeetCode实现strStr函数,算法,算法

二、暴力穷解法

  思路分析:首先判断字符串是否合法,然后利用for循环,取出子字符串利用compare函数进行比较。
  程序如下

class Solution {
public:
	// 复杂度n * m
	int strStr(string haystack, string needle) {
		if (haystack.size() < needle.size()) return -1;	
		if (!needle.size()) return 0; // needle为空返回0
		for (int i = 0; i < haystack.size(); ++i) {
			string substr = haystack.substr(i, needle.size());
			if (!needle.compare(substr)) return i;
		}
		return -1;
	}
};

复杂度分析:

  • 时间复杂度: O ( n ∗ m ) O(n * m) O(nm),假设haystack的长度为n,needle的长度为m,for循环的复杂度为n,当中调用了compare函数,它是逐字符比较的,复杂度为m,因此总复杂度为 O ( n ∗ m ) O(n * m) O(nm)
  • 空间复杂度: O ( 1 ) O(1) O(1)

三、KMP算法

  思路分析:这个算法比较著名了,简单来讲就是建立前缀表,然后进行匹配,匹配失败就根据前缀表找到下一个模式串的位置。具体的思路可以看看笔者的这片文章【算法与数据结构】字符串匹配算法。
  程序如下

	// KMP算法
    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[i] == s[j + 1]) { // 找到相同的前后缀
                j++;
            }
            next[i] = j; // 将j(前缀的长度)赋给next[i]
            
        }
    }
    int strStr2(string haystack, string needle) {
        if (needle.size() == 0) {
            return 0;
        }
        int* next = new int[needle.size()];
        //int next[needle.size()]; 
        getNext(next, needle);
        //my_print(next, needle.size(), "前缀表:");
        int j = -1; // // 因为next数组里记录的起始位置为-1
        for (int i = 0; i < haystack.size(); i++) { // 注意i就从0开始
            while (j >= 0 && haystack[i] != needle[j + 1]) { // 不匹配
                j = next[j]; // j 寻找之前匹配的位置
            }
            if (haystack[i] == needle[j + 1]) { // 匹配,j和i同时向后移动
                j++; // i的增加在for循环里
            }
            if (j == (needle.size() - 1)) { // 文本串s里出现了模式串t
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }

复杂度分析:

  • 时间复杂度: O ( n + m ) O(n + m) O(n+m),其中n为文本串长度,m为模式串长度,因为在匹配的过程中,根据前缀表不断调整匹配的位置,可以看出匹配的过程是O(n),之前还要单独生成next数组,时间复杂度是O(m)。所以整个KMP算法的时间复杂度是O(n+m)的。
  • 空间复杂度: O ( m ) O(m) O(m),需要额外的空间存放大小为m的数组。

四、Sunday算法

  思路分析:思路部分大家也可以看笔者的这篇文章【算法与数据结构】字符串匹配算法。第二个版本的算法相对来说简洁快速一些。
  程序如下

    // Sunday算法
    int find_single_char(char c, const string& needle) {   
        for (int i = needle.size() - 1; i >= 0; --i) {  // 找最右端的字符,因此从后往前循环
            if (c == needle[i]) return i; 
        }
        return -1;
    }
    int strStr3(string haystack, string needle) {
        if (haystack.size() < needle.size()) return -1;     // 检查合法性
        if (!needle.size()) return 0;                       // needle为空返回0      
        for (int i = 0; i <= haystack.size() - needle.size(); ) {
            for (int j = 0; j < needle.size(); ++j) {
                if (needle[j] != haystack[i + j]) {     // 匹配失败                
                    int k = find_single_char(haystack[i + needle.size()], needle);   // 文本字符串末尾的下一位字符串
                    if (k == -1)   i += needle.size() + 1;  // 模式串向右移动 模式串长度 + 1 
                    else i += needle.size() - k;            // 向右移动 模式串最右端的该字符到末尾的距离+1
                    break;
                }     
                if (j == needle.size() - 1) return i;   // 匹配成功
            }
        }
        return -1;
    }
    // 查找算法用哈希表代替的Sunday算法   
    int strStr4(string haystack, string needle) {
        if (haystack.size() < needle.size()) return -1;     // 检查合法性
        if (!needle.size()) return 0;                       // needle为空返回0  

        int shift_table[128] = { 0 };       // 128为ASCII码表长度
        for (int i = 0; i < 128; i++) {     // 偏移表默认值设置为 模式串长度 + 1
            shift_table[i] = needle.size() + 1;
        }
        for (int i = 0; i < needle.size(); i++) {	// 如果有重复字符也会覆盖,确保shift_table是 模式串最右端的字符到末尾的距离 + 1
            shift_table[needle[i]] = needle.size() - i;
        }

        int s = 0; // 文本串初始位置
        int j;
        while (s <= haystack.size() - needle.size()) {
            j = 0;
            while (haystack[s + j] == needle[j]) { 
                ++j;
                if (j >= needle.size()) return s;   // 匹配成功
            }
            // 找到主串中当前跟模式串匹配的最末字符的下一个字符
            // 在模式串中出现最后的位置
            // 所需要从(模式串末尾+1)移动到该位置的步数
            s += shift_table[haystack[s + needle.size()]];
        }
        return -1;
    }

复杂度分析:

  • 时间复杂度: 平均时间复杂度为 O ( n ) O(n) O(n),最坏情况时间复杂度为 O ( n ∗ m ) O(n*m) O(nm)
  • 空间复杂度: O ( 1 ) O(1) O(1)

五、完整代码

# include <iostream>
# include <string>
using namespace std;

void my_print(int* arr, int arr_len, string str) {
    cout << str << endl;
    for (int i = 0; i < arr_len; ++i) {
        cout << arr[i] << ' ';
    }
    cout << endl;
}

class Solution {
public:
	// 暴力穷解
	// 复杂度n * m
	int strStr(string haystack, string needle) {
		if (haystack.size() < needle.size()) return -1;	
		if (!needle.size()) return 0; // needle为空返回0
		for (int i = 0; i < haystack.size(); ++i) {
			string substr = haystack.substr(i, needle.size());
			if (!needle.compare(substr)) return i;
		}
		return -1;
	}

	// KMP算法
    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[i] == s[j + 1]) { // 找到相同的前后缀
                j++;
            }
            next[i] = j; // 将j(前缀的长度)赋给next[i]
            
        }
    }
    int strStr2(string haystack, string needle) {
        if (needle.size() == 0) {
            return 0;
        }
        int* next = new int[needle.size()];
        //int next[needle.size()]; 
        getNext(next, needle);
        //my_print(next, needle.size(), "前缀表:");
        int j = -1; // // 因为next数组里记录的起始位置为-1
        for (int i = 0; i < haystack.size(); i++) { // 注意i就从0开始
            while (j >= 0 && haystack[i] != needle[j + 1]) { // 不匹配
                j = next[j]; // j 寻找之前匹配的位置
            }
            if (haystack[i] == needle[j + 1]) { // 匹配,j和i同时向后移动
                j++; // i的增加在for循环里
            }
            if (j == (needle.size() - 1)) { // 文本串s里出现了模式串t
                return (i - needle.size() + 1);
            }
        }
        return -1;
    }

    // Sunday算法
    int find_single_char(char c, const string& needle) {   
        for (int i = needle.size() - 1; i >= 0; --i) {  // 找最右端的字符,因此从后往前循环
            if (c == needle[i]) return i; 
        }
        return -1;
    }
    int strStr3(string haystack, string needle) {
        if (haystack.size() < needle.size()) return -1;     // 检查合法性
        if (!needle.size()) return 0;                       // needle为空返回0      
        for (int i = 0; i <= haystack.size() - needle.size(); ) {
            for (int j = 0; j < needle.size(); ++j) {
                if (needle[j] != haystack[i + j]) {     // 匹配失败                
                    int k = find_single_char(haystack[i + needle.size()], needle);   // 文本字符串末尾的下一位字符串
                    if (k == -1)   i += needle.size() + 1;  // 模式串向右移动 模式串长度 + 1 
                    else i += needle.size() - k;            // 向右移动 模式串最右端的该字符到末尾的距离+1
                    break;
                }     
                if (j == needle.size() - 1) return i;   // 匹配成功
            }
        }
        return -1;
    }
};

int main()
{
	//string haystack = "sadbutsad";
	//string needle = "sad";
	//string haystack = "abc";
	//string needle = "c";
    //string haystack = "substring searching algorithm";
    //string needle = "search";
    //string haystack = "hello";
    //string needle = "ll";
    //string haystack = "mississippi";
    //string needle = "issi";
    string haystack = "aabaaaababaababaa";
    string needle = "bbbb";
	int k = 2;
	Solution s1;
	cout << "目标字符串:\n" << "“" << haystack << "”" << endl;
	int result = s1.strStr3(haystack, needle);
	cout << "查找子串结果:\n" << result << endl;
	system("pause");
	return 0;
}

end文章来源地址https://www.toymoban.com/news/detail-520034.html

到了这里,关于【算法与数据结构】28、LeetCode实现strStr函数的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构算法leetcode刷题练习(1)

    给定一个三角形 triangle ,找出自顶向下的最小路径和。 每一步只能移动到下一行中相邻的结点上。相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。也就是说,如果正位于当前行的下标 i ,那么下一步可以移动到下一行的下标

    2023年04月24日
    浏览(40)
  • 【算法与数据结构】62、LeetCode不同路径

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :机器人只能向下或者向右移动,那么到达(i,j)位置的路径和(i-1,j)以及(i,j-1)有关。那么我们就得到的动态规划的表达式 d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i ] [ j − 1 ] dp[i][

    2024年01月18日
    浏览(54)
  • 【算法与数据结构】343、LeetCode整数拆分

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :博主做这道题的时候一直在思考,如何找到 k k k 个正整数, k k k 究竟为多少合适。从数学的逻辑上来说,将 n n n 均分为 k k k 个数之后, k k k 个数的乘积为最大(类似于相同周长

    2024年01月17日
    浏览(39)
  • 【算法与数据结构】474、LeetCode一和零

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :本题要找strs数组的最大子集,这个子集最多含有 m m m 个0和 n n n 个1。本题也可以抽象成一个01背包的问题。其中,strs内的元素就是物品,而 m m m 和 n n n 就是背包的维度。 d p [

    2024年01月22日
    浏览(32)
  • 【算法与数据结构】494、LeetCode目标和

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :本题和这道题【算法与数据结构】1049、LeetCode 最后一块石头的重量 II类似,同样可以转换成01背包问题。下面开始论述。假设添加正号的整数子集和为 p o s i t i v e positive p os i t

    2024年01月20日
    浏览(31)
  • 【算法与数据结构】377、LeetCode组合总和 Ⅳ

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :本题明面上说是组合,实际上指的是排列。动态规划排列组合背包问题需要考虑遍历顺序。 d p [ i ] dp[i] d p [ i ] 指的是nums数组中总和为target的元素排列的个数。 d p [ i ] dp[i] d p [

    2024年01月23日
    浏览(30)
  • 【python与数据结构】(leetcode算法预备知识)

    笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ 1.数字类型: 整数(int):表示整数值,例如 1、-5、100。 浮点数(float):表示带有小数部分的数字,例如 3.14、-0.5、2.0。 复数(complex):表示实部和虚部的复数,例如 2+3j。 2.布尔类型(bool): 表示真(True)或假(

    2024年02月08日
    浏览(27)
  • 【算法与数据结构】226、LeetCode翻转二叉树

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :这道题的思路很简单,本质上就是遍历每一个节点,然后交换左右节点。我们可以用前中后遍历或者是层次遍历法来做,参考这两篇文章,【算法与数据结构】144、94、145LeetCode二

    2024年02月16日
    浏览(30)
  • 【算法与数据结构】654、LeetCode最大二叉树

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :【算法与数据结构】106、LeetCode从中序与后序遍历序列构造二叉树这两道题有些类似,相关代码可以互相参考,本题明示了要用递归来做,那么递归三要素不可缺少: 输入参数和返

    2024年02月09日
    浏览(34)
  • 【算法与数据结构】63、LeetCode不同路径 II

    所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。    思路分析 :参考【算法与数据结构】62、LeetCode不同路径的题目,可以发现本题仅仅是多了障碍物。我们还是用动态规划来做。有障碍物的地方无法到达,因此路径数量为0,只需要将障碍物位

    2024年02月02日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包