Swift 数据结构与算法( ) + Leetcode 掘金 #日新计划更文活动
题目
给你两个字符串:ransomNote
和 magazine
,判断 ransomNote
能不能由 magazine
里面的字符构成。
如果可以,返回 true
;否则返回 false
。
magazine
中的每个字符只能在 ransomNote
中使用一次。
示例 1:
输入: ransomNote = "a", magazine = "b"
输出: false
示例 2:
输入: ransomNote = "aa", magazine = "ab"
输出: false
示例 3:
输入: ransomNote = "aa", magazine = "aab"
输出: true
提示:
1 <= ransomNote.length, magazine.length <= 105
ransomNote
和magazine
由小写英文字母组成
解题思路🙋🏻 ♀️
1. 题目分析
题目要求:
给定两个字符串:ransomNote
和 magazine
,需要判断 ransomNote
是否可以由 magazine
中的字符构成。重要的是,magazine
中的每个字符只能在 ransomNote
中使用一次。
函数返回值:
函数返回一个布尔值 - 如果可以构成,返回 true
,否则返回 false
。
题目类型分析: 这是一个字符串处理问题,更具体地说,它与字符计数有关。在LeetCode上,这种问题通常可以使用哈希表或数组来解决,尤其是当输入只包括小写字母时,数组是一个非常有效的工具,因为它可以用于跟踪每个字母的计数。
2. 解题思路
我们可以使用一个数组(长度为26,代表26个小写字母)来跟踪 magazine
中每个字母的出现次数。然后,当遍历 ransomNote
时,我们可以简单地减少相应字母的计数。如果某个时刻,某个字母的计数变为负数,这意味着 magazine
没有足够的字符来构成 ransomNote
,因此我们可以立即返回 false
。
流程图:
ransomNote = "aa", magazine = "aab"
1. 初始化一个数组 letterCounts = [0, 0, ..., 0] (26个0)
2. 遍历 magazine:
a. 'a' -> letterCounts[0] = 1
b. 'a' -> letterCounts[0] = 2
c. 'b' -> letterCounts[1] = 1
3. 遍历 ransomNote:
a. 'a' -> letterCounts[0] 减少 1 -> 值为 1
b. 'a' -> letterCounts[0] 减少 1 -> 值为 0
4. 所有字符计数都大于等于0,返回 true
使用数组来跟踪字符的出现次数是这个问题的关键。这种方法确保了我们在 O(n) 的时间内解决了这个问题,其中 n 是 magazine
的长度。
边界思考🤔
代码
func canConstruct(_ ransomNote: String, _ magazine: String) -> Bool {
// 初始化一个长度为26的数组,代表26个小写英文字母。
// 这个数组将用于存储magazine中每个字母出现的次数。
var letterCounts = [Int](repeating: 0, count: 26)
// 开始遍历magazine字符串中的每个字符
for char in magazine {
// 这里我们通过计算字母与'a'的ASCII差值来得到该字母在数组中的索引。
// 例如,如果char是'a',则index为0;如果char是'b',则index为1,以此类推。
let index = Int(char.asciiValue! - Character("a").asciiValue!)
// 在对应索引的位置增加1,代表这个字符在magazine中出现了一次
letterCounts[index] += 1
}
// 接下来,我们遍历ransomNote字符串中的每个字符
for char in ransomNote {
// 同样地,我们计算该字符在数组中的索引
let index = Int(char.asciiValue! - Character("a").asciiValue!)
// 检查在letterCounts数组中该字符的计数
// 如果为0,说明magazine中没有足够的字符来组成ransomNote
if letterCounts[index] == 0 {
return false
}
// 如果magazine中有该字符,我们在数组中减少该字符的计数
letterCounts[index] -= 1
}
// 如果函数还没有返回false,那么说明ransomNote中的所有字符都在magazine中
// 因此我们返回true,表示ransomNote可以由magazine组成
return true
}
时空复杂度分析
时间复杂度: O(n + m)
- 其中 n 是
magazine
的长度,m 是ransomNote
的长度。因为我们只遍历了两个字符串各一次,所以时间复杂度是两者长度之和。
空间复杂度: O(1)
- 无论输入字符串的长度如何,我们都使用了一个固定大小(长度为26)的数组。因此,空间复杂度是常数。
错误与反思
概念
使用场景与应用
1. 时空复杂度
2. 需要学习的概念和实际应用
主要概念:
- 字符计数: 使用数组或哈希表来跟踪每个字符的出现次数。
- 固定大小的数组优化: 当知道可能的字符集大小时(例如26个小写字母),可以使用固定大小的数组来优化空间。
实际应用场景:
- 文档相似度检测: 通过比较两篇文章中的词汇和字符分布,可以估计两篇文章的相似度。技术点包括字符计数和哈希表。
- 拼写检查器: 通过比较单词与字典中的单词的字符分布,可以推荐正确的拼写。技术点包括字符计数和编辑距离算法。
- 数据压缩: 通过字符的频率,可以为常用的字符分配较短的编码,从而实现数据压缩。技术点包括字符计数和霍夫曼编码。
3. iOS app 开发的实际应用
- 搜索优化: 在iOS应用中,当用户在搜索框中输入查询时,可以使用字符计数来快速过滤和排序结果,确保最相关的结果排在最前面。
- 内容推荐系统: 基于用户的阅读习惯,应用可以分析用户喜欢的文章中的词汇和字符分布,从而为用户推荐相似的内容。
- 键盘输入建议: iOS的自动完成功能可以基于字符计数来预测用户可能会输入的下一个字符或单词,从而加速输入速度。