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

news2024/11/26 2:37:52

文章目录

  • 一、题目
  • 二、暴力穷解法
  • 三、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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

:hover悬浮在元素上方时,不出现黑边

给element plus的下拉菜单设置悬浮样式 .el-dropdown :hover{border: none;outline: none; } border:node 表示元素本身的边框没有了 outline:none 表示元素的轮廓线没有了

K8S集群管理

集群管理 1 集群管理1.1 节点管理1.1.1 令牌管理1.1.2 集群扩缩容1.1.3 集群升级1.1.4 证书管理 1.2 数据管理1.2.1 ETCD基础1.2.2 ETCD实践1.2.3 备份还原1.2.4 ETCD集群 1 集群管理 1.1 节点管理 1.1.1 令牌管理 学习目标 这一节&#xff0c;我们从 令牌基础、令牌实践、…

【Part 2】博物馆防刷票小程序接入无感验证--跳转式接入

前文在这里&#xff1a; 【Part 1】现在去博物馆都预约不上了&#xff0c;黑产多少有点疯狂了 前面这篇文章讲到目前博物馆的门票基本被黄牛薅没了&#xff0c;我们普通人只能通过黄牛去买票&#xff0c;并且讲到了预约小程序如何通过插件式接入无感验证。 这篇文章我们继续讲…

数据结构之 时间复杂度与空间复杂度

目录 1&#xff1a;什么是时间复杂度和空间复杂度 2&#xff1a;时间复杂度与空间复杂度求法 3&#xff1a;几个典型时间复杂度与空间复杂度的分析 1&#xff1a;什么是时间复杂度与空间复杂度 首先对于我们所写的程序来说&#xff0c;为了评估一个算法的好与坏我们需要通过…

用*打印一个空心三角形

通过找规律发现&#xff0c;有*的位置坐标是&#xff08;0&#xff0c;3&#xff09;&#xff08;1&#xff0c;2&#xff09;&#xff08;1&#xff0c;4&#xff09;&#xff08;2&#xff0c;1&#xff09;&#xff08;2&#xff0c;5&#xff09;&#xff0c;三角形左边坐标…

Python爬虫使用代理IP的实现

使用爬虫时&#xff0c;如果目标网站对访问的速度或次数要求较高&#xff0c;那么你的 IP 就很容易被封掉&#xff0c;也就意味着在一段时间内无法再进行下一步的工作。这时候代理 IP 能够给我们带来很大的便利&#xff0c;不管网站怎么封&#xff0c;只要能找到一个新的代理 I…

软考:中级软件设计师:计算机存储结构,cache,局部性原理,RAM和ROM,磁盘结构和计算

软考&#xff1a;中级软件设计师:计算机存储结构 提示&#xff1a;系列被面试官问的问题&#xff0c;我自己当时不会&#xff0c;所以下来自己复盘一下&#xff0c;认真学习和总结&#xff0c;以应对未来更多的可能性 关于互联网大厂的笔试面试&#xff0c;都是需要细心准备的…

2023年铁矿石行业研究报告

第一章 行业概况 铁矿石行业是指从地壳中开采铁矿石&#xff0c;经过破碎、筛选、洗选等步骤&#xff0c;使其达到工业上可用的标准的一整套产业。这个行业涵盖了从开采到运输&#xff0c;再到冶炼和销售的全过程。它是全球制造业&#xff0c;尤其是钢铁工业的基础。 铁矿石的…

org.apache.commons(commons-csv)下载CSV增加BOM头

网络说明 代码说明 依赖 <!--https://mvnrepository.com/artifact/org.apache.commons/commons-csv--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.8</version><…

C++学习笔记-第9单元 异常处理

第9单元 异常处理 文章目录 第9单元 异常处理单元导读9.1 异常处理概览9.1.1 异常处理概览9.1.2 异常处理机制的优点 9.2 异常匹配与内建异常类9.2.1 异常匹配与异常类9.2.2 内建异常类 9.3 自定义异常类与多重捕获9.3.1 自定义异常类9.3.2 捕获多种无关异常9.3.3 捕获派生异常…

C#Winform 中tabcontrol 美化实例

本篇讲解tabcontrol 美化实例,我们项目开发中为了保持项目界面的风格的一致性,需要美化tabcontrol控件,系统自带的控件样式不能更改,无法满足需求。 先看效果 实现过程 创建winform项目,添加组件类控件 修改名称,点击添加 cs代码 using System.Drawing; using Sys…

TX Text Control .NET for WPF 31.SP3 Crack

.NET WPF 应用程序的文档处理 将文档编辑、创建和 PDF 生成添加到您的 WPF 应用程序中。 视窗用户界面 功能齐全的文档编辑器 TX Text Control 是一款免版税、完全可编程的丰富编辑控件&#xff0c;它在专为 Visual Studio 设计的可重用组件中为开发人员提供了广泛的文字处理功…

Spring Native 实现 0.059s 启动一个SpringBoot项目!

前言 最近自己用Spring Cloud Alibaba做了一个微服务架构的项目&#xff0c;部署的时候遇到了难题&#xff1a;内存不够。目前该项目有7个微服务&#xff0c;因为我只有一台阿里云的服务器(2C 4G)&#xff0c;所以我只能把所有的微服务部署在一台服务器上&#xff0c;部署方式…

uni-App踩坑记录

​ 1、uni自己封装的axios在真机中失效&#xff0c;发不出请求 uniapp中使用axios 需要配置适配器 (添加适配器有点费劲&#xff0c;直接封装uni自带请求也可以) axios-adapter-uniapp传送门 axios.defaults.adapter function(config) { //自己定义个适配器&#xff0c;用来…

JS获取表单保存的时候发送到server端的xml

var dataXml Xrm.Page.data.entity.getDataXml(); alert(dataXml); JS获取表单保存的时候发送到server端的xml。

一文读懂Unreal Engine游戏引擎如何提高数字孪生场景渲染能力

以下案例来自于《数字孪生世界白皮书&#xff08;2023版&#xff09;》 领取方式&#xff1a;公众号「EasyV数字孪生」后台回复「白皮书」即可领取&#xff01; Unreal Engine&#xff08;下文简称为UE&#xff09;&#xff0c;是一款由Epic Games开发的游戏引擎&#xff0c;…

100天精通Python(可视化篇)——第94天:Pyecharts绘制多种炫酷散点图(参数说明+代码实战)

文章目录 专栏导读一、Scatter&#xff08;散点图&#xff09;1. add函数2. 数据项 二、基础气泡图三、多维度散点图四、添加分割线五、动态涟漪散点图六、不同形状散点图七、3D散点图 专栏导读 &#x1f525;&#x1f525;本文已收录于《100天精通Python从入门到就业》&#…

CentOS7 图形化方式安装 Oracle19C

CentOS7 图形化方式安装 Oracle19C 操作系统&#xff1a;CentOS7 Oracle&#xff1a; 19C 远程连接工具&#xff1a;Xmanager Power Suite 7 安装常用工具和依赖 yum -y install vim tar net-tools wget perl python3 readline* deltarpm python-deltarpm \zip unzip bc compa…

天翎群晖NAS为全文检索插翅起飞

编者按&#xff1a;企业的文档资料随着企业的业务发展会越来越多&#xff0c;想要某个资料的时候&#xff0c;最怕找不到想要的资料&#xff0c;这时KMS的全文检索功能就非常重要了&#xff0c;只需只言片语的零星关键字&#xff0c;查找文档没压力。 关键词&#xff1a;全文检…

Liunx服务器磁盘挂载

一&#xff1a;查看磁盘信息 [rootxxx ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sr0 11:0 1 1024M 0 rom vda 253:0 0 100G 0 disk ├─vda1 …