LeetCode刷题笔记 字符串 字符串匹配
28 实现 strStr()
输入一个母字符串和一个子字符串,输出一个整数,表示子字符串在母字符串的位置,若不存在则返回-1。
输入:haystack = “hello”, needle = “ll”
输出:2
解析:
解决本题一种简单的思路是暴力匹配:首先将子串和母串左端对齐;然后逐个比较对应的字符,如果发现不匹配则将子串开始匹配位置相对于母串后移动一位,同时将比较指针回溯到子串头部;重复匹配过程,直到找到对应子串,不存在则返回 -1。
class Solution {public:int strStr(string haystack, string needle) {int m = haystack.length(), n = needle.length();for(int i=0;i+n<=m;++i){bool flag = true;for(int j=0;j<n;++j){if(haystack[j+i]!=needle[j]){flag = false;break;}}if(flag){return i;}}return -1;}
};
上述方法的比较指针不断回溯的过程会增加时间复杂度,一种优化方法就是 KMP 算法。KMP 算法的核心思想就是通过寻找已匹配部分子串中的相同前后缀,在遇到字符不匹配的情况时,直接将子串从前缀部分移动到后缀,避免比较指针直接回溯到子串头部。
如下例所示,母串和子串在第 8 个字符出现不匹配,下一步匹配操作:如果使用暴力匹配则是将匹配指针回溯到子串头部并往后移动一位再次寻找与母串左端对齐;如果使用KMP算法,可以看到子串已经匹配的相同最长前后缀为ABC
,直接将整个子串从前缀位置移动到后缀,一次性移动四位,避免比较指针从头再开始匹配。
原串 | 暴力匹配 | KMP 算法 |
---|---|---|
ABCFABCFABCA
|
ABCFABCFABCA
|
ABCFABCFABCA
|
ABCFABCA
|
0ABCFABCA
|
0000ABCFABCA
|
KMP 算法的关键是在子串中找到最长前后缀,这里可以采用动态规划的思想:
设置状态:构建一个数组 next[i]
表示子串中对应位置 i 之前的部分串中最长前后缀的长度。
状态转移方程:对于位置 i,如果下一位前后缀相同,更新相同最大前后缀的长度;如果下一位不同,则将向前回溯。
初始情况:如果仅有一个字符不存在前后缀,next[0]=-1
。前缀指针从 -1 位置开始,后缀指针从 1 位置开始遍历子串。
// 计算前缀表 next
void getNext(string needle, vector<int>& next){int head = -1;next[0] = -1;for(int tail = 1; tail<needle.length();++tail){// 如果下一位不同,往前回溯,回溯到没有前缀为止(head=-1)while(head>-1 && needle[head+1]!=needle[tail]){head = next[head];}// 如果下一位相同,更新相同的最大前缀和最大后缀长,同时移动前缀指针if(needle[head+1]==needle[tail]){++head;}next[head] = head;}
}
一个上述计算ABCFABCA
前缀表的例子:
next 索引 | 部分子串 | 最长前缀最后一个元素的位置 next[i] |
---|---|---|
0 | A | -1 |
1 | AB | -1 |
2 | ABC | -1 |
3 | ABCF | -1 |
4 | ABCFA | 0 |
5 | ABCFAB | 1 |
6 | ABCFABC | 2 |
7 | ABCFABCA | 2 |
class Solution {public:// 计算前缀表 nextvoid getNext(string needle, vector<int>& next){int head = -1;next[0] = -1;for(int tail=1;tail<needle.length();++tail){// 如果下一位不同,往前回溯,回溯到没有前缀为止(head=-1)while(head>-1 && needle[head+1]!=needle[tail]){head = next[head];}if(needle[head+1]==needle[tail]){++head;}next[tail] = head;}}int strStr(string haystack, string needle) {int cur = -1;int m = haystack.size(), n = needle.size();// 子串为空返回 0 if(n==0) return 0;// 获取前缀表vector<int> next(n,-1);getNext(needle,next);for(int i=0;i<m;++i){while(cur>-1 && haystack[i]!=needle[cur+1]){cur = next[cur];}if(haystack[i]==needle[cur+1]){++cur;}// 说明=cur移动到needle的最末端,此时i也指向母串中匹配子串的最后一个位置,返回此时匹配子串最左端的位置if(cur == n-1){return i - cur;}}return -1;}
};
参考资料
LeetCode 101:和你一起轻松刷题(C++) 第 12 章 令人头大的字符串
LeetCode刷题笔记 字符串 字符串匹配相关推荐
- Leetcode刷题笔记-字符串总结
首先明确两个概念:子串与子序列. 比如一个字符串"aaabbc"的一个子串为"aaa".而"abc"是它的一个子序列.即子串必须是在字符串中 ...
- LeetCode刷题笔记汇总
LeetCode刷题笔记汇总 第一次刷LeetCode写的一些笔记. 1.两数之和 3.无重复字符的最长子串 15.三数之和 18.四数之和 19.删除链表的倒数第 N 个结点 20.有效的括号 21 ...
- LeetCode刷题笔记第6题:Z字形变换
LeetCode刷题笔记第6题:Z字形变换 想法: 要完成字符串根据给定的行数从上往下,从左到右完成Z字形排列.当只有一行时直接返回原字符串,当行数大于1时,先以行数构建一个行数数值个空字符串的列表, ...
- LeetCode刷题笔记(算法思想 四)
LeetCode刷题笔记(算法思想 四) 七.动态规划 斐波那契数列 70. 爬楼梯 198. 打家劫舍 213. 打家劫舍 II 信件错排 母牛生产 矩阵路径 64. 最小路径和 62. 不同路径 ...
- LeetCode刷题笔记第171题: Excel 表列序号
LeetCode刷题笔记第171题: Excel 表列序号 想法: 给你一个字符串 columnTitle,表示 Excel 表格中的列名称.返回 该列名称对应的列序号.此题单个字符的对应值与ASCI ...
- 卷进大厂系列之LeetCode刷题笔记:二分查找(简单)
LeetCode刷题笔记:二分查找(简单) 学算法,刷力扣,加油卷,进大厂! 题目描述 涉及算法 题目解答 学算法,刷力扣,加油卷,进大厂! 题目描述 力扣题目链接 给定一个 n 个元素有序的(升序) ...
- LeetCode刷题笔记2——数组2
LeetCode刷题笔记2--数组2 重塑数组 题目 在 MATLAB 中,有一个非常有用的函数 reshape ,它可以将一个 m x n 矩阵重塑为另一个大小不同(r x c)的新矩阵,但保留其原 ...
- 小何同学的leetcode刷题笔记 基础篇(01)整数反转
小何同学的leetcode刷题笔记 基础篇(01)整数反转[07] *** [01]数学取余法*** 对数字进行数位操作时,常见的方法便是用取余的方法提取出各位数字,再进行操作 操作(1):对10取余 ...
- 【leetcode刷题笔记】动态规划
#[leetcode刷题笔记]动态规划 石子游戏 public boolean stoneGame(int[] piles) {int N = piles.length;// dp[i][j] is ...
最新文章
- Adobe推出HTML5动画设计工具Edge
- 节能信标组比赛过程中直流电源设置
- 住房要注意用电安全-记录一下失火
- java中两个整形相除,向上取整
- Netty源码分析第7章(编码器和写数据)----第2节: MessageToByteEncoder
- C语言实现通用链表初步(三)----单元测试
- totorisgit与git两种方式pushpull文件
- JSONObject中optString和getString等的区别
- 【NOI2011】兔兔与蛋蛋的游戏【二分图博弈】
- 训练dnn_[预训练语言模型专题] MT-DNN(KD) : 预训练、多任务、知识蒸馏的结合
- powershell自动化操作AD域、Exchange邮箱系列(3)—重要的模块/API介绍Get-Aduser Get-Mailbox
- 苹果鼠标右键怎么按_UG经验技巧案例17把UG命令设置到Shift+Ctrl+鼠标的左、中、右三键及右键长、按右键的方法...
- 小试牛刀——搭建一个周报管理系统
- 计算机网络-详细版-王道
- 单例模式之懒汉式(三种代码实现)
- 第一章 SDN介绍 (附件1 )【华为SDN产业链分析】
- 阿里云——弹性公网IP
- python vimdiff_vimdiff的常用命令
- python练习生|这是你熟悉的第一行代码吗?(附python安装教程(win))
- Android 定制自己的launcher