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

文章目录

  • 一、题目
  • 二、暴力穷解法
  • 三、KMP算法
  • 四、Sunday算法
  • 五、完整代码

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

一、题目

在这里插入图片描述

二、暴力穷解法

  思路分析:首先判断字符串是否合法,然后利用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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/34698.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2023年全国节能宣传“节能低碳,你我同行”主题有奖竞答

2023年的7月10日至16日是第33个全国节能宣传周&#xff0c;主题是“节能降碳&#xff0c;你我同行”。 为践行低碳生活&#xff0c;切实做到节能降碳&#xff0c;各大企事业单位纷纷举办“节能低碳&#xff0c;你我同行”主题2023年全国节能宣传有奖竞答。 有奖知识竞答活动方…

Prometheus实现自定义指标监控

1、Prometheus实现自定义指标监控 前面我们已经通过 PrometheusGrafana 实现了监控&#xff0c;可以在 Grafana 上看到对应的 SpringBoot 应用信息了&#xff0c; 通过这些信息我们可以对 SpringBoot 应用有更全面的监控。 但是如果我们需要对一些业务指标做监控&#xff0c;…

【AI实战】从零开始搭建中文 LLaMA-33B 语言模型 Chinese-LLaMA-Alpaca-33B

【AI实战】从零开始搭建中文 LLaMA-33B 语言模型 Chinese-LLaMA-Alpaca-33B 简介环境配置环境搭建依赖安装 代码及模型权重拉取拉取 Chinese-LLaMA-Alpaca拉取 llama-30b-hf 模型权重及代码拉取 chinese-llama-lora-33b 模型权重及代码 合并模型权重先转换 pth 类型的模型权重&…

只出现一次的数字

题目链接 只出现一次的数字 题目描述 注意点 1 < nums.length < 30000-30000 < nums[i] < 30000除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次 解答思路 最初想到使用一种数据结构将元素存储起来&#xff0c;但是空间复杂度为O(n)&#xff0…

【花雕】全国青少年机器人技术一级考试备考实操搭建手册6

随着科技的不断进步&#xff0c;机器人技术已经成为了一个重要的领域。在这个领域中&#xff0c;机械结构是机器人设计中至关重要的一部分&#xff0c;它决定了机器人的形态、运动方式和工作效率。对于青少年机器人爱好者来说&#xff0c;了解机械结构的基础知识&#xff0c;掌…

大语言模型的百家齐放

基础语言模型 概念 基础语言模型是指只在大规模文本语料中进行了预训练的模型&#xff0c;未经过指令和下游任务微调、以及人类反馈等任何对齐优化。 如何理解 只包含纯粹的语言表示能力,没有指导性或特定目标。 只在大量无标注文本上进行无监督预训练,用于学习语言表示。 …

git 新建分支,切换分支,上传到远程分支

git 在使用的过程中&#xff0c;有的时候我们需要更换一个分支才存贮数据&#xff0c;作为版本的一个迭代或者是阶段性成果的一个里程碑。 如何来做操作呢&#xff1f; 在git中&#xff0c;可利用checkout命令转换分支&#xff0c;该命令的作用就是切换分支或恢复工作树文件&a…

【微信小程序开发】第 9 课 - 小程序的协同工作和发布

欢迎来到博主 Apeiron 的博客&#xff0c;祝您旅程愉快 &#xff01; 时止则止&#xff0c;时行则行。动静不失其时&#xff0c;其道光明。 目录 1、协同工作 1.1、了解权限管理需求 1.2、了解项目成员的组织结构 1.3、小程序的开发流程 2、小程序成员管理 2.1、成员管…

Nftables栈溢出漏洞(CVE-2022-1015)复现

背景介绍 Nftables Nftables 是一个基于内核的包过滤框架&#xff0c;用于 Linux 操作系统中的网络安全和防火墙功能。nftables 的设计目标是提供一种更简单、更灵活和更高效的方式来管理网络数据包的流量。 钩子点&#xff08;Hook Point&#xff09; 钩子点的作用是拦截数…

DMDSC共享存储集群启动、关闭及介绍

DMDSC介绍 DM 共享存储数据库集群&#xff08;DMDSC&#xff09;。DM共享存储数据库集群&#xff0c;允许多个数据库实例同时访问、操作同一数据库&#xff0c;具有高可用、高性能、负载均衡等特性。DMDSC 支持故障自动切换和故障自动重加入&#xff0c;某一个数据库实例故障后…

使用GeoPandas进行地理空间数据可视化

大家好&#xff0c;在当今数据驱动的世界中&#xff0c;将信息可视化到地图上可以提供有价值的见解&#xff0c;帮助有效地传达复杂的模式。GeoPandas是一个建立在pandas和shapely之上的Python库&#xff0c;使用户能够通过将地理空间数据与各种变量合并来创建令人惊叹的地图。…

深度学习(23)——YOLO系列(2)

深度学习&#xff08;23&#xff09;——YOLO系列&#xff08;2&#xff09; 文章目录 深度学习&#xff08;23&#xff09;——YOLO系列&#xff08;2&#xff09;1. model2. dataset3. utils4. test/detect5. detect全过程 今天先写YOLO v3的代码&#xff0c;后面再出v5&…

C语言:猜凶手

题目&#xff1a; 日本某地发生了一件谋杀案&#xff0c;警察通过排查确定杀人凶手必为4个嫌疑犯的一个。 以下为4个嫌疑犯的供词: A说&#xff1a;不是我。 B说&#xff1a;是C。 C说&#xff1a;是D。 D说&#xff1a;C在胡说 已知3个人说了真话&#xff0c;1个人说的是假话。…

2023,中国电商重回元老时代

中国的历史上不缺“太上皇”&#xff0c;但“太上皇”再度站到台前的很少。公元1457年&#xff0c;被囚禁在南宫的“太上皇”朱祁镇复位&#xff0c;上演了中国历史上少见的南宫复辟。而危机时刻被推举为皇帝的朱祁钰&#xff0c;后来的庙号是代宗&#xff0c;阴阳怪气十足。 …

Spark Sql 4/5

4. 用户自定义函数 通过spark.udf功能用户可以自定义函数。 4.1用户自定义UDF函数 Shellscala> val df spark.read.json("examples/src/main/resources/people.json")df: org.apache.spark.sql.DataFrame [age: bigint, name: string]​scala> df.show()--…

分布式运用——监控平台 Zabbix

分布式运用——监控平台 Zabbix 一、监控平台种类二、我们今天介绍Linux操作系统的传统监控平台——zabbix 6.0版本1.zabbix 是什么&#xff1f;2.**zabbix 监控原理&#xff1a;**3.Zabbix 6.0 新特性&#xff1a;4. Zabbix 6.0 功能组件&#xff1a;5.数据库6.Web 界面7.Zabb…

.NetCore gRpc 客户端与服务端的单工通信Demo

文章目录 .NetCore gRpc 客户端与服务端的单工通信Demo服务端方式一方式二 客户端proto协议文件syntax "proto3";import "google/protobuf/empty.proto";serviceproto3与.netCore 的类型对应日期和时间可为 null 的类型字节小数为 Protobuf 创建自定义 de…

Rust in Action笔记 第八章 网络

P253的图展示了网络各层用到的协议Box<dyn std::error::Error>表示一个指针指向的实现了标准错误库的类型&#xff0c;dyn表明这是一个特征对象&#xff08;trait object&#xff09;&#xff0c;是rust里多态的一种实现方式&#xff1b;特征对象和模板对象&#xff08;g…

物化视图功能验证

物化视图(Materialized View)和视图(View)类似&#xff0c;也是一个视图名字对应一个SQL查询查询语句。不同之处在于&#xff1a;物化视图定义时使用了额外的关键字materialized&#xff0c; 它把结果集保存在起来&#xff0c;查询的时候直接读取保存的结果集&#xff0c;而不必…

Zabbix安装

Zabbix6.0 一&#xff1a;zabbix 是什么&#xff1f;二&#xff1a;Zabbix 6.0 新特性&#xff1a;1、Zabbix server高可用防止硬件故障或计划维护期的停机&#xff1a;2、Zabbix 6.0 LTS新增Kubernetes监控功能&#xff0c;可以在Kubernetes系统从多个维度采集指标&#xff1a…