Swift 数据结构与算法(50) + Leetcode125. 验证回文串

76 阅读5分钟

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

题目

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true **;否则,返回 **false **。

 

示例 1:

输入: s = "A man, a plan, a canal: Panama"
输出: true
解释: "amanaplanacanalpanama" 是回文串。

示例 2:

输入: s = "race a car"
输出: false
解释: "raceacar" 不是回文串。

示例 3:

输入: s = " "
输出: true
解释: 在移除非字母数字字符之后,s 是一个空字符串 "" 。
由于空字符串正着反着读都一样,所以是回文串。

 

提示:

  • 1 <= s.length <= 2 * 105
  • s 仅由可打印的 ASCII 字符组成

解题思路🙋🏻‍ ♀️

回文字符串是指一个字符串正向读取和反向读取都相同的字符串。简而言之,它是一个对称的字符串,您可以将其想象为沿其中心线对折,两边都是相同的。

例如:

  1. "radar" - 如果您从左到右读或从右到左读,都是 "radar"。
  2. "level" - 同样地,无论您如何读它,它都是 "level"。
  3. "deified" - 正向和反向都是 "deified"。
  4. "A man, a plan, a canal, Panama!" - 如果您移除所有的标点、空格并将大写字母转换为小写,您会得到 "amanaplanacanalpanama",这也是一个回文。

回文不仅限于英文。许多语言和文化都有自己的回文短语、诗歌和故事。

回文在计算机科学中也有其应用,特别是在字符串处理中,例如在 DNA 序列分析中查找回文序列,这些序列在生物学中有特殊的意义。

1. 题目分析

题目要求: 题目给定了一个字符串 s,要求我们判断它是否是一个回文串。在判断之前,我们需要执行以下步骤:

  • 将所有大写字符转换为小写字符
  • 移除所有非字母数字字符

函数的返回值应该是一个布尔值,表示字符串是否是回文串。

题目类型: 这是一个字符串处理题目,更具体地说,它与回文字符串识别有关。

常用套路: 对于这种类型的题目,常用的解题套路是使用双指针技术。一个指针从字符串的开头开始,另一个指针从字符串的末尾开始。我们比较这两个指针指向的字符,如果它们都是相同的,我们就移动指针继续比较,直到两个指针相遇或交叉。

2. 解题思路和演示

思路:

  1. 将输入的字符串转换为小写。
  2. 使用两个指针,一个从字符串的开头开始,另一个从字符串的末尾开始。
  3. 移动指针,直到它们指向字母或数字字符。
  4. 比较这两个指针指向的字符。如果它们不同,返回 false
  5. 如果所有的字符都匹配,返回 true

演示:

考虑例子 s = "A man, a plan, a canal: Panama"

  1. 转换为小写: s = "a man, a plan, a canal: panama"
  2. 使用双指针技术,初始化 left = 0right = s.length - 1
  3. 移动 leftright 指针,直到它们指向字母或数字字符。
  4. 比较 s[left]s[right]:
    • 如果它们相同,继续移动指针。
    • 如果它们不同,返回 false
  5. 重复上述步骤,直到 left >= right
  6. 如果所有字符都匹配,返回 true

边界思考🤔

代码

func isPalindrome(_ s: String) -> Bool {
    // 将所有字符转换为小写
    let lowercased = s.lowercased()
    
    // 过滤字符串,只保留字母和数字
    let validChars = lowercased.filter { $0.isLetter || $0.isNumber }
    
    // 使用双指针技术检查字符串是否为回文
    var left = 0
    var right = validChars.count - 1
    
    while left < right {
        // 检查左右指针指向的字符是否相同
        if validChars[validChars.index(validChars.startIndex, offsetBy: left)] !=
            validChars[validChars.index(validChars.startIndex, offsetBy: right)] {
            return false
        }
        
        // 移动左右指针
        left += 1
        right -= 1
    }
    
    // 如果所有字符检查都通过,则为回文
    return true
}

时空复杂度分析

时间复杂度: O(n)

  • 字符串的小写转换需要 O(n)。
  • 过滤字符串也需要 O(n)。
  • 使用双指针技术遍历字符串需要 O(n)。

因此,总的时间复杂度是 O(n)。

空间复杂度: O(n)

  • 这是因为我们创建了一个 validChars 字符串来保存过滤后的字符。

错误与反思

问题

  1. 代码没有考虑大小写。题目要求比较时不区分大小写,但代码会区分大小写。
  2. 代码没有移除非字母数字字符。题目要求只考虑字母和数字字符。

因此,你的函数可能会对某些输入返回错误的结果。

分析: 当你编写代码时,可能主要关注了如何确定字符串是否为回文,而忽略了题目的其他要求。这是一个常见的错误,尤其是当我们对某个概念或技术(如双指针)非常熟悉时,我们可能会过于关注这个技术,而忽略其他细节。

如何避免

  1. 仔细阅读题目:确保你理解了所有的要求和限制。
  2. 列出步骤:在开始编写代码之前,先列出处理输入数据的所有步骤。
  3. 测试:在提交答案之前,使用各种测试用例,特别是边界情况,测试你的代码。

修复代码: 为了修复这个问题,我们需要添加额外的逻辑来处理大小写和非字母数字字符。可以考虑使用 Swift 的 lowercased() 方法和 isLetterisNumber 属性。

这行代码是 Swift 语言中的一种简洁、函数式编程写法。它使用了 Swift 的 filter 方法,这是 StringArray 等集合类型的一个方法。filter 方法的目的是根据某个条件筛选集合中的元素。

让我们一步步地分解这行代码:

  1. filter 方法:

    • filter 接收一个闭包作为参数。这个闭包定义了筛选条件。
    • 对于集合中的每一个元素,这个闭包都会被调用,并传入当前的元素作为参数。
    • 如果闭包返回 true,那么这个元素会被包含在筛选后的结果中。如果返回 false,则不会。
  2. 闭包 { $0.isLetter || $0.isNumber }:

    • 在这个闭包中,$0 代表当前正在被筛选的元素。
    • .isLetterCharacter 的一个属性,它返回一个布尔值,表示这个字符是否是一个字母。
    • .isNumber 同样是 Character 的一个属性,它返回一个布尔值,表示这个字符是否是一个数字。
    • || 是一个逻辑"或"运算符,所以整个闭包的意思是:如果这个字符是一个字母或一个数字,返回 true,否则返回 false

所以,validChars 将会是一个只包含字母和数字的新字符串,所有其他字符都会被过滤掉。

例如,如果 lowercased"a man, a plan, a canal: panama",那么 validChars 将会是 "amanaplanacanalpanama"

概念

使用场景与应用

2. 关键概念及其应用

关键概念:

  • 字符串处理: 如何转换、过滤和处理字符串是本题的核心。
  • 双指针技术: 这是一种常见的技术,用于有效地遍历和比较字符串或数组。

实际应用:

  1. 搜索引擎:搜索引擎需要处理和比较大量的文本数据。例如,过滤和转换文本是构建搜索索引的关键步骤。
    • 技术点:文本预处理、文本比较、索引构建。
  2. 文本编辑器:文本编辑器或 IDEs 可能会提供回文检查或其他文本分析功能。
    • 技术点:文本解析、字符串操作、用户界面设计。

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

  1. 输入验证:在用户输入数据(如用户名、密码、电子邮件地址等)时,app 需要验证输入内容。字符串处理和验证是这里的关键。

    • 示例:密码强度检查、电子邮件格式验证。
  2. 搜索功能:许多 iOS apps 提供内部搜索功能,允许用户搜索内容。

    • 示例:在记事本 app 中搜索特定的文本、在音乐 app 中搜索歌曲。
    • 技术点:字符串比较、数据过滤、用户界面更新。
  3. 文本格式化:在显示内容或接收用户输入时,app 可能需要格式化文本(如日期、货币、数字等)。

    • 示例:将日期从 "YYYY-MM-DD" 格式转换为 "MM/DD/YYYY"、货币格式化。
    • 技术点:字符串转换、区域设置处理、用户界面更新。