Swift 数据结构与算法(44) + Leetcode28. 找出字符串中第一个匹配项的下标

114 阅读4分钟

Swift 数据结构与算法( ) + Leetcode 掘金 #日新计划更文活动

题目

28. 找出字符串中第一个匹配项的下标

给你两个字符串 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 <= 104
  • haystack 和 needle 仅由小写英文字符组成

解题思路🙋🏻‍ ♀️

KMP 算法

截屏2023-08-19 22.25.47.png

截屏2023-08-19 22.25.56.png

边界思考🤔

代码

使用基础比较.

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 中截取从 startend 的子字符串,并与 needle 进行比较。如果它们相等,那么我们找到了一个匹配,所以我们返回当前的开始位置 i

总结:

这段代码的核心思想是遍历 haystack 中所有可能的与 needle 匹配的子字符串的开始位置,并逐个进行比较。为了有效地截取 haystack 中的子字符串,我们需要使用 Swift 的 String.Index 类型,这就是为什么我们需要 index(_:offsetBy:) 这样的方法。

错误与反思

概念

使用场景与应用

1. 最需要学习的概念:

  1. 字符串索引操作:在 Swift 中,直接使用整数作为字符串索引是不允许的。我们需要使用 String.Index 类型以及相关的方法,如 index(_:offsetBy:),来获取和操作字符串中的字符。
  2. 子字符串操作:Swift 提供了方便的范围操作符 ..< 来获取子字符串。
  3. 字符串匹配:这是一个常见的问题,尤其是在文本处理中。理解如何使用暴力匹配或更高效的算法(如 KMP)是很重要的。

2. 实际应用场景及技术点:

  • 搜索引擎:当用户在搜索框中输入关键词时,后台会使用字符串匹配算法来查找相关的结果。
    • 技术点:高效的字符串匹配算法,如 KMP 或 Boyer-Moore 算法。
  • 文本编辑器:用户可以在文本编辑器中查找或替换特定的字符串。
    • 技术点:字符串匹配、替换操作。
  • 数据验证:在处理用户输入或文件内容时,我们可能需要检查是否包含某些特定的字符串或模式。
    • 技术点:正则表达式、字符串匹配。

3. iOS app 开发的实际使用场景:

  1. 搜索功能:在许多 iOS 应用中,如联系人、音乐或邮件应用,用户可以搜索特定的内容。这需要使用字符串匹配来快速找到与用户输入匹配的结果。

    • 技术应用:使用字符串匹配算法在数据集中查找用户输入的关键词。
  2. 自动补全和建议:当用户在搜索框中输入内容时,应用可能会提供相关的建议或自动补全。

    • 技术应用:使用字符串匹配和前缀树(Trie)来提供与用户输入相关的建议。
  3. 高亮显示:在阅读或编辑文本时,应用可能会提供一个功能,允许用户高亮显示与特定关键词匹配的所有文本。

    • 技术应用:使用字符串匹配找到所有匹配的位置,并在 UI 上高亮显示这些位置。
  4. 内容过滤:在社交应用或评论系统中,应用可能需要过滤掉一些不适当的内容或关键词。

    • 技术应用:使用字符串匹配检查用户输入的内容是否包含黑名单中的关键词。