题目
力扣题目链接
https://leetcode.cn/problems/ransom-note/
题目说明
给你两个字符串: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 ≤ r a n s o m N o t e . l e n g t h , m a g a z i n e . l e n g t h ≤ 105 1 \leq ransomNote.length, magazine.length \leq 105 1≤ransomNote.length,magazine.length≤105
ransomNote
和magazine
由小写英文字母组成
通过次数198,496提交次数316,170
解题方法
方法一:字符统计
题目要求使用字符串 magazine
中的字符来构建新的字符串 ransomNote
,且ransomNote
中的每个字符只能使用一次,只需要满足字符串 magazine
中的每个英文字母 (’a’-’z’)
的统计次数都大于等于 ransomNote
中相同字母的统计次数即可。
- 如果字符串
magazine
的长度小于字符串ransomNote
的长度,则我们可以肯定magazine
无法构成ransomNote
,此时直接返回false
。 - 首先统计
magazine
中每个英文字母a
的次数cnt[a]
,再遍历统计 \textit{ransomNote}ransomNote 中每个英文字母的次数,如果发现ransomNote
中存在某个英文字母c
的统计次数大于magazine
中该字母统计次数cnt[c]
,则此时我们直接返回false
。
代码
python
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
if len(ransomNote) > len(magazine):
return False
return not collections.Counter(ransomNote) - collections.Counter(magazine)
C++
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
if (ransomNote.size() > magazine.size()) {
return false;
}
vector<int> cnt(26);
for (auto & c : magazine) {
cnt[c - 'a']++;
}
for (auto & c : ransomNote) {
cnt[c - 'a']--;
if (cnt[c - 'a'] < 0) {
return false;
}
}
return true;
}
};
复杂度分析
-
时间复杂度: O ( m + n ) O(m + n) O(m+n),其中
m
是字符串ransomNote
的长度,n
是字符串magazine
的长度,我们只需要遍历两个字符一次即可。 -
空间复杂度: O ( ∣ S ∣ ) O(|S|) O(∣S∣),
S
是字符集,这道题中S
为全部小写英语字母,因此 ∣ S ∣ = 26 |S| = 26 ∣S∣=26
方法二:哈希表
因为题目所只有小写字母,那可以采用空间换取时间的哈希策略, 用一个长度为26的数组还记录magazine里字母出现的次数。
然后再用ransomNote
去验证这个数组是否包含了ransomNote
所需要的所有字母。
依然是数组在哈希法中的应用。
一些同学可能想,用数组干啥,都用map
完事了,其实在本题的情况下,使用map
的空间消耗要比数组大一些的,因为map
要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!
代码如下:
C++
// 时间复杂度: O(n)
// 空间复杂度:O(1)
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
int record[26] = {0};
//add
if (ransomNote.size() > magazine.size()) {
return false;
}
for (int i = 0; i < magazine.length(); i++) {
// 通过recode数据记录 magazine里各个字符出现次数
record[magazine[i]-'a'] ++;
}
for (int j = 0; j < ransomNote.length(); j++) {
// 遍历ransomNote,在record里对应的字符个数做--操作
record[ransomNote[j]-'a']--;
// 如果小于零说明ransomNote里出现的字符,magazine没有
if(record[ransomNote[j]-'a'] < 0) {
return false;
}
}
return true;
}
};
python
class Solution:
def canConstruct(self, ransomNote: str, magazine: str) -> bool:
arr = [0] * 26
for x in magazine:
arr[ord(x) - ord('a')] += 1
for x in ransomNote:
if arr[ord(x) - ord('a')] == 0:
return False
else:
arr[ord(x) - ord('a')] -= 1
return True