Swift 数据结构与算法( ) + Leetcode 掘金 #日新计划更文活动
题目
给你两个字符串 haystack 和 needle ,请你在 haystack 字符串中找出 needle 字符串的第一个匹配项的下标(下标从 0 开始)。如果 needle 不是 haystack 的一部分,则返回 -1 ****。
示例 1:
输入: haystack = "sadbutsad", needle = "sad"
输出: 0
解释: "sad" 在下标 0 和 6 处匹配。
第一个匹配项的下标是 0 ,所以返回 0 。
示例 2:
输入: haystack = "leetcode", needle = "leeto"
输出: -1
解释: "leeto" 没有在 "leetcode" 中出现,所以返回 -1 。
提示:
1 <= haystack.length, needle.length <= 104haystack和needle仅由小写英文字符组成
解题思路🙋🏻 ♀️
KMP 算法
边界思考🤔
代码
使用基础比较.
class Solution {
func strStr(_ haystack: String, _ needle: String) -> Int {
// 判断 needle 是否为空,为空直接返回 0
if needle.count <= 0 {
return -1
}
// 判断 haystack 的长度是否小于 needle 的长度,如果是,则直接返回 -1,因为 needle 无法在 haystack 中匹配
if needle.count > haystack.count {
return -1
}
// 使用 for 循环从 haystack 的第一个字符开始进行匹配
for item in 0...(haystack.count - needle.count) {
// 1. start
let start = haystack.index(haystack.startIndex, offsetBy: item)
let end = haystack.index(start, offsetBy: needle.count)
//
if haystack[start..<end] == needle {
return item
}
}
// 如果循环结束还没有找到匹配的字符串,则返回 -1
return -1
}
}
时空复杂度分析
代码分析
首先,我们的目标是在 haystack 中找到 needle 的位置。为了实现这一点,我们将遍历 haystack 并检查从每个位置开始的子字符串是否与 needle 匹配。
1. 外层循环:
for i in 0...(haystack.count - needle.count)
这个循环是为了遍历 haystack 中可能与 needle 匹配的所有子字符串的开始位置。
为什么是 haystack.count - needle.count?考虑以下情况:
- 如果
haystack的长度为 10,needle的长度为 3,那么最后一个可能的匹配开始位置是 7(从0开始数),因为从位置 7、8、9 的子字符串长度正好为 3。 - 如果我们尝试从位置 8 或 9 开始,那么子字符串的长度将小于 3,因此无法与
needle匹配。
2. 获取子字符串的开始和结束位置:
let start = haystack.index(haystack.startIndex, offsetBy: i)
这行代码获取 haystack 中从位置 i 开始的子字符串的开始位置。
let end = haystack.index(start, offsetBy: needle.count)
这行代码获取从 start 开始、长度为 needle.count 的子字符串的结束位置。
3. 比较子字符串与 needle:
if haystack[start..<end] == needle {
return i
}
这里,我们使用范围操作符 ..< 从 haystack 中截取从 start 到 end 的子字符串,并与 needle 进行比较。如果它们相等,那么我们找到了一个匹配,所以我们返回当前的开始位置 i。
总结:
这段代码的核心思想是遍历 haystack 中所有可能的与 needle 匹配的子字符串的开始位置,并逐个进行比较。为了有效地截取 haystack 中的子字符串,我们需要使用 Swift 的 String.Index 类型,这就是为什么我们需要 index(_:offsetBy:) 这样的方法。
错误与反思
概念
使用场景与应用
1. 最需要学习的概念:
- 字符串索引操作:在 Swift 中,直接使用整数作为字符串索引是不允许的。我们需要使用
String.Index类型以及相关的方法,如index(_:offsetBy:),来获取和操作字符串中的字符。 - 子字符串操作:Swift 提供了方便的范围操作符
..<来获取子字符串。 - 字符串匹配:这是一个常见的问题,尤其是在文本处理中。理解如何使用暴力匹配或更高效的算法(如 KMP)是很重要的。
2. 实际应用场景及技术点:
- 搜索引擎:当用户在搜索框中输入关键词时,后台会使用字符串匹配算法来查找相关的结果。
- 技术点:高效的字符串匹配算法,如 KMP 或 Boyer-Moore 算法。
- 文本编辑器:用户可以在文本编辑器中查找或替换特定的字符串。
- 技术点:字符串匹配、替换操作。
- 数据验证:在处理用户输入或文件内容时,我们可能需要检查是否包含某些特定的字符串或模式。
- 技术点:正则表达式、字符串匹配。
3. iOS app 开发的实际使用场景:
-
搜索功能:在许多 iOS 应用中,如联系人、音乐或邮件应用,用户可以搜索特定的内容。这需要使用字符串匹配来快速找到与用户输入匹配的结果。
- 技术应用:使用字符串匹配算法在数据集中查找用户输入的关键词。
-
自动补全和建议:当用户在搜索框中输入内容时,应用可能会提供相关的建议或自动补全。
- 技术应用:使用字符串匹配和前缀树(Trie)来提供与用户输入相关的建议。
-
高亮显示:在阅读或编辑文本时,应用可能会提供一个功能,允许用户高亮显示与特定关键词匹配的所有文本。
- 技术应用:使用字符串匹配找到所有匹配的位置,并在 UI 上高亮显示这些位置。
-
内容过滤:在社交应用或评论系统中,应用可能需要过滤掉一些不适当的内容或关键词。
- 技术应用:使用字符串匹配检查用户输入的内容是否包含黑名单中的关键词。