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 字符组成
解题思路🙋🏻 ♀️
回文字符串是指一个字符串正向读取和反向读取都相同的字符串。简而言之,它是一个对称的字符串,您可以将其想象为沿其中心线对折,两边都是相同的。
例如:
- "radar" - 如果您从左到右读或从右到左读,都是 "radar"。
- "level" - 同样地,无论您如何读它,它都是 "level"。
- "deified" - 正向和反向都是 "deified"。
- "A man, a plan, a canal, Panama!" - 如果您移除所有的标点、空格并将大写字母转换为小写,您会得到 "amanaplanacanalpanama",这也是一个回文。
回文不仅限于英文。许多语言和文化都有自己的回文短语、诗歌和故事。
回文在计算机科学中也有其应用,特别是在字符串处理中,例如在 DNA 序列分析中查找回文序列,这些序列在生物学中有特殊的意义。
1. 题目分析
题目要求:
题目给定了一个字符串 s
,要求我们判断它是否是一个回文串。在判断之前,我们需要执行以下步骤:
- 将所有大写字符转换为小写字符
- 移除所有非字母数字字符
函数的返回值应该是一个布尔值,表示字符串是否是回文串。
题目类型: 这是一个字符串处理题目,更具体地说,它与回文字符串识别有关。
常用套路: 对于这种类型的题目,常用的解题套路是使用双指针技术。一个指针从字符串的开头开始,另一个指针从字符串的末尾开始。我们比较这两个指针指向的字符,如果它们都是相同的,我们就移动指针继续比较,直到两个指针相遇或交叉。
2. 解题思路和演示
思路:
- 将输入的字符串转换为小写。
- 使用两个指针,一个从字符串的开头开始,另一个从字符串的末尾开始。
- 移动指针,直到它们指向字母或数字字符。
- 比较这两个指针指向的字符。如果它们不同,返回
false
。 - 如果所有的字符都匹配,返回
true
。
演示:
考虑例子 s = "A man, a plan, a canal: Panama"
:
- 转换为小写:
s = "a man, a plan, a canal: panama"
- 使用双指针技术,初始化
left = 0
和right = s.length - 1
- 移动
left
和right
指针,直到它们指向字母或数字字符。 - 比较
s[left]
和s[right]
:- 如果它们相同,继续移动指针。
- 如果它们不同,返回
false
。
- 重复上述步骤,直到
left >= right
。 - 如果所有字符都匹配,返回
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
字符串来保存过滤后的字符。
错误与反思
问题:
- 代码没有考虑大小写。题目要求比较时不区分大小写,但代码会区分大小写。
- 代码没有移除非字母数字字符。题目要求只考虑字母和数字字符。
因此,你的函数可能会对某些输入返回错误的结果。
分析: 当你编写代码时,可能主要关注了如何确定字符串是否为回文,而忽略了题目的其他要求。这是一个常见的错误,尤其是当我们对某个概念或技术(如双指针)非常熟悉时,我们可能会过于关注这个技术,而忽略其他细节。
如何避免:
- 仔细阅读题目:确保你理解了所有的要求和限制。
- 列出步骤:在开始编写代码之前,先列出处理输入数据的所有步骤。
- 测试:在提交答案之前,使用各种测试用例,特别是边界情况,测试你的代码。
修复代码: 为了修复这个问题,我们需要添加额外的逻辑来处理大小写和非字母数字字符。可以考虑使用 Swift 的 lowercased()
方法和 isLetter
、isNumber
属性。
这行代码是 Swift 语言中的一种简洁、函数式编程写法。它使用了 Swift 的 filter
方法,这是 String
和 Array
等集合类型的一个方法。filter
方法的目的是根据某个条件筛选集合中的元素。
让我们一步步地分解这行代码:
-
filter
方法:filter
接收一个闭包作为参数。这个闭包定义了筛选条件。- 对于集合中的每一个元素,这个闭包都会被调用,并传入当前的元素作为参数。
- 如果闭包返回
true
,那么这个元素会被包含在筛选后的结果中。如果返回false
,则不会。
-
闭包
{ $0.isLetter || $0.isNumber }
:- 在这个闭包中,
$0
代表当前正在被筛选的元素。 .isLetter
是Character
的一个属性,它返回一个布尔值,表示这个字符是否是一个字母。.isNumber
同样是Character
的一个属性,它返回一个布尔值,表示这个字符是否是一个数字。||
是一个逻辑"或"运算符,所以整个闭包的意思是:如果这个字符是一个字母或一个数字,返回true
,否则返回false
。
- 在这个闭包中,
所以,validChars
将会是一个只包含字母和数字的新字符串,所有其他字符都会被过滤掉。
例如,如果 lowercased
是 "a man, a plan, a canal: panama"
,那么 validChars
将会是 "amanaplanacanalpanama"
。
概念
使用场景与应用
2. 关键概念及其应用
关键概念:
- 字符串处理: 如何转换、过滤和处理字符串是本题的核心。
- 双指针技术: 这是一种常见的技术,用于有效地遍历和比较字符串或数组。
实际应用:
- 搜索引擎:搜索引擎需要处理和比较大量的文本数据。例如,过滤和转换文本是构建搜索索引的关键步骤。
- 技术点:文本预处理、文本比较、索引构建。
- 文本编辑器:文本编辑器或 IDEs 可能会提供回文检查或其他文本分析功能。
- 技术点:文本解析、字符串操作、用户界面设计。
3. iOS app 开发的实际使用场景
-
输入验证:在用户输入数据(如用户名、密码、电子邮件地址等)时,app 需要验证输入内容。字符串处理和验证是这里的关键。
- 示例:密码强度检查、电子邮件格式验证。
-
搜索功能:许多 iOS apps 提供内部搜索功能,允许用户搜索内容。
- 示例:在记事本 app 中搜索特定的文本、在音乐 app 中搜索歌曲。
- 技术点:字符串比较、数据过滤、用户界面更新。
-
文本格式化:在显示内容或接收用户输入时,app 可能需要格式化文本(如日期、货币、数字等)。
- 示例:将日期从 "YYYY-MM-DD" 格式转换为 "MM/DD/YYYY"、货币格式化。
- 技术点:字符串转换、区域设置处理、用户界面更新。