算法Day09 | KMP,28. 实现 strStr() ,459.重复的子字符串

news/2024/11/25 21:58:46/

Day09

    • KMP
    • 28. 实现 strStr()
    • 459.重复的子字符串

KMP

KMP是三个人人名缩写,用于在文本字符串text中搜索pattern字符串,返回在text中第一出现的位置。
算法做法就是在暴力匹配的基础上加速匹配。通过对pattern字符串求next数组(该数组也成为前缀表),来跳过text部分匹配。next数组是为了实现跳过部分字符串的功能来设计的。暴力匹配的时间复杂度为 O ( m × n ) O(m\times n) O(m×n),KMP的时间复杂度为 O ( m + n ) O(m + n) O(m+n)

如何求出next数组?通过求出pattern所有字符子串的最长相等前、后缀构成next数组。具体实现可能存在其他加工,如对next数组减一,位移一位等。


假设有abbabbk,对于字符k有一个信息,k之前字符串中,前、后缀匹配最大长度为3。其中前、后缀为k前字符串的子字符串,即不包含整体abbabb字符串。为什么不包含,因为是next数组对应长度的下标不能等于它本身,k是第6个元素,next[k] != 6,而且整体字符串也一定相等,无意义。

int next[] = {-1/*人为规定*/, 0/*根据定义*/, 0/1, ...};

求next数组是往前跳的过程,最后用到前三个元素来得到最终数组。

d4779b1360e9149fe4265c7afc4f631
0b8fd333c19d3f228d872827b21f74c
b7ef04f09a89f04411565a5cbf1b608
0e1a3cf49c881dba57b070df93deaff

010f77584f1ff064d7ac05c0b700a6e

//通过回跳得到完整的next数组
void getNext(const string& s, int* next) {//初始化next数组next[0] = -1;//由于next数组是看之前的字符。当s只有一个长度时,之前没有字符。所以只能是在此跳出。if (s.size() == 1) return;next[1] = 0;int i = 2;//遍历next数组,next数组的下标int cn = 0;//回跳的值,也是next数组的值while (i < s.size()) {if (s[i - 1] == s[cn]) {//next[i]是用next[i-1]求next[i++] = ++cn;//i为cn+1} else if (cn > 0) {//cn还可以往前跳cn = next[cn];} else {next[i++] = 0;//next[i]为0}}
}
int KMP(const string& text, const string& pattern) {int next[pattern.size()];getNext(pattern, next);int i = 0/*遍历text*/, j = 0/*遍历pattern*/;//保持i的位置不变,通过调节j的位置来匹配字符,直到i匹配完毕,匹配i+1while (i < text.size() && j < pattern.size()) {if (text[i] == pattern[j]) {//匹配上了,对比下一个i++;j++;} else if (j != 0) {//j还可以往回跳j = next[j];} else {//j跳不动了。i匹配不上,匹配i+1i++;}}//是否是根据j跳出的边界//j跳出while表示已经配出相同的了,才会导致j++到出界//不是因为j跳出的while,说明没有找到return j == pattern.size() ? i - j : -1;//i-j对应找到的下标
}

对第六行代码 if (s.size() == 1) return;,没有这一句,当字符串s只有一个字符时,next数组应该直接返回next[1] = {-1},如果不跳出,则返回next[2] = {-1, 0};所以会造成dynamic-stack-buffer-overflow
本地编译器来说,不加这一句也能通过,应该是给优化掉了。
LeetCode没有优化掉,不过还是应该填上这一句,保证代码的稳定。


28. 实现 strStr()

题目链接:28. 实现 strStr()
KMP代码同上

class Solution {
public:void getNext(const string &s, int* next) {next[0] = -1;if (s.size() == 1) return;next[1] = 0;int i = 2;int cn = 0;while (i < s.size()) {if (s[i - 1] == s[cn]) {next[i++] = ++cn;} else if (cn > 0) {cn = next[cn];} else {next[i++] = 0;}}}int strStr(string haystack, string needle) {int next[needle.size()];getNext(needle, next);int i = 0;int j = 0;while (i < haystack.size() && j < needle.size()) {if (haystack[i] == needle[j]) {i++;j++;} else if (j > 0) {j = next[j];} else {i++;}}return j == needle.size() ? i - j : -1;}
};

459.重复的子字符串

题目链接: 459.重复的子字符串
移动匹配
字符串s中有重复,s+s也会包含serase()时间复杂度为 O ( n ) O(n) O(n)

class Solution {
public:bool repeatedSubstringPattern(string s) {string ss = s + s;ss.erase(ss.begin());ss.erase(ss.end() - 1);return ss.find(s) != string::npos;}
};

KMP
用到了next数组,根据next数组的构成原则可得,重复的子字符串 = 字符串 - 最长相等前后缀。如果没有最长相等前后缀,直接返回false

class Solution {
public:void getNext(const string& s, int* next) {next[0] = -1;if (s.size() == 1) return;next[1] = 0;int i = 2;int cn = 0;while (i < s.size()) {if (s[i - 1] == s[cn]) {next[i++] = ++cn;} else if (cn > 0) {cn = next[cn];} else {next[i++] = 0;}}}bool repeatedSubstringPattern(string s) {int len = s.size();int next[len + 1];//多一位是因为next数组的构造不同,需要整个字符串的next数组getNext(s + 'A'/*只要加一个不满足s要求的字符都行*/, next);//比如字符串是"aba",变成"abaA"//return (next[len] != 0 && len % (len - next[len]) == 0) ? true : false;//简化为return next[len] != 0/*等于0,直接跳出为false*/&& len % (len - next[len]) == 0;}
};


http://www.ppmy.cn/news/75059.html

相关文章

gitlab建立新分支提交,cherry-pick部分更新

gitlab介绍 GitLab是一个基于Git的在线代码托管和协作平台&#xff0c;提供源代码管理、单元测试、CI/CD构建、代码审查等功能。它是一个开放源代码的Git仓库管理系统&#xff0c;使用 Ruby on Rails 构建GitLab 不仅具有自己的 Git 仓库管理系统&#xff0c;还具有很多其他的…

SpringSecurity权限管理基本概念和整体架构介绍

文章目录 一、权限管理1、认证2、授权3、对权限控制&#xff0c;现有的解决方案 二、SpringSecurity简介1、官方定义2、历史 三、整体架构1、认证AuthenticationManagerAuthenticationSecurityContextHolder 2、授权AccessDecisionManagerAccessDecisionVoterConfigAttribute 一…

容器目录挂载原理

前言 就我目前的对容器的了解, 使用namespace技术实现隔离, 使用cgroups技术实现资源限制. 但是具体是如何实现却从未深究过. 闲来无事, 挑其中的Mount Namespace来康康, 容器是如何实现目录隔离的. 目录隔离 在耗子叔的这篇文章中对此技术进行了介绍. 在c函数库中, 可通过…

Useraws的ec2服务器安装mysql

要在AWS EC2实例上安装MySQL&#xff0c;您可以按照以下步骤进行操作&#xff1a; 1. 启动EC2实例&#xff1a;登录到AWS控制台&#xff0c;启动一个EC2实例。确保您选择了合适的实例类型和配置&#xff0c;并确保具有所需的存储容量。 2. 连接到EC2实例&#xff1a;使用SSH客…

解决城市内涝的措施有哪些?需要用到哪些监测设备?

随着城市化的不断推进&#xff0c;城市内涝问题日益凸显。极端天气事件如暴雨、台风等对城市基础设施和居民生活造成了严重影响。那么&#xff0c;解决城市内涝的措施有哪些?需要用到哪些监测设备?针对上述问题&#xff0c;本文会为大家一一进行讲解。 解决城市内涝的措施有哪…

不要被别人影响,踏实做自己的事

预习 笔记 复习 做题 专注 效率 记忆 欢迎观看我的博客&#xff0c;如有问题交流&#xff0c;欢迎评论区留言&#xff0c;一定尽快回复&#xff01;&#xff08;大家可以去看我的专栏&#xff0c;是所有文章的目录&#xff09;   文章字体风格&#xff1a; 红色文字表示&#…

Scala学习(七)---面向对象特质

文章目录 1.面向对象特质(Trait)2.特质声明2.1 特质的特点2.2 特质冲突2.3 特质叠加2.4 特质自身类型2.5 特质和抽象类的区别扩展 1.面向对象特质(Trait) 在Scala语言中&#xff0c;采用特质trait(特征)来代替接口的概念&#xff0c;也就是说&#xff0c;多个类具有相同的特质…

MarkDown语法1

MarkDown语法1 段落样式 RUNOOB.COM GOOLE.COM 字体 斜体文本 斜体文本 粗体文本 粗斜体文本 粗斜体文本 高亮文本 高亮加粗文本 分割线 删除线 RUNOOB.COM GOOGLE.COM BAIDU.COM 下划线 带下划线文本 脚注 MarkDown列表 MarkDown支持有序列表和无序列表。…