题目:
给定两个字符串 s
和 t
,编写一个函数来判断 t
是否是 s
的 字母异位词。
示例 1:
ini
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
ini
输入: s = "rat", t = "car"
输出: false
提示:
1 <= s.length, t.length <= 5 * 104
s
和t
仅包含小写字母
进阶: 如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
解释:
- 字母异位词(anagram)指的是两个字符串,长度相同,且每个字符出现的次数都一样,顺序可以不同。
- car 和 car 两个字符串完全一样,当然每个字符出现的次数都一样,所以它们是字母异位词。
实现:
1. 这是我之前学python的时候做的
python
class Solution(object):
def isAnagram(self, s, t):
"""
:type s: str
:type t: str
:rtype: bool
"""
# 母异位词必须是两个长度相同的字符串
if len(s) == len(t):
for i in s:
# 每个字符出现的次数都一样
if i not in t or s.count(i) != t.count(i):
# 如果有t中的字符不存在s中的某一字符,或者数量不一样,就返回False
return False
return True
else:
return False
2. 下面是我用JavaScript的实现
2.1 用ASII码实现
- 之所以会想到使用ASII码来实现原因主要是提示里有一条:
s
和t
仅包含小写字母,这是我的逻辑,不一定正确,大家可以帮忙指出问题 - 另外我想的是这两个字符串里面包含的字符是完全相同的(
只是顺序不同罢了
),所以只要这两个字符串所有的字符转成ASII码值,然后综合相等,就可以了
js
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
// 母异位词必须是两个长度相同的字符串
if (s.length !== t.length) return false
let sum1 = 0;
let sum2 = 0;
for (const char of s) {
sum1 += char.charCodeAt();
}
for (const char of t) {
sum2 += char.charCodeAt();
}
return sum1 === sum2
}
结果:
- 两个示例都通过了
- 但是有了反例:
s
="ggii",t
="eekk" - 40 / 53 个通过的测试用例
2.2 用和python实现相同的方法
- js没有直接统计一个字符在字符串中出现的次数的方法,我还去特意查了一下可以用str.split(char).length + 1来统计
- 这里我犯了一个错误:我用了
for...in
来遍历字符串s
了,注意:for...in遍历字符串只会遍历字符串的索引
js
/**
* @param {string} s
* @param {string} t
* @return {boolean}
*/
var isAnagram = function(s, t) {
if (s.length !== t.length) return false
for (let char of s) {
if (!t.includes(char) || s.split(char).length !== t.split(char).length) {
return false
}
}
return true
};
结果:
- 两个示例都通过了
- 但是当面对超长字符串时,超时了,直接给浏览器干崩溃了
- 其实时间复杂度只是O(n^2)
2.3 Deepseek给2.2的优化版本
js
var isAnagram = function(s, t) {
if (s.length !== t.length) return false;
const count = {};
// 统计s的字符
for (let char of s) {
count[char] = (count[char] || 0) + 1;
}
// 验证t的字符
for (let char of t) {
if (!count[char]) return false;
count[char]--;
}
return true;
};
结果
- 通过率100%
复杂度对比
方法 | 时间复杂度 | 空间复杂度 |
---|---|---|
原始实现 | O(n²) | O(n)(split创建临时数组) |
优化实现 | O(n) | O(1)(因为字母表固定大小) |
关键点:当处理字符串问题时,尽量避免在循环内使用includes()
、split()
等O(n)操作。
2.4 结合排序实现
- 这个我一开始就想到了,但是我觉的直接使用sort排序是不是就像我昨天使用indexOf一样,就是说,用方法的话,那还能叫算法吗
- 但是,这是一种方法,leedCode通过率100%
js
/**
* 时间复杂度O(n log n)
* split(): O(n)
* sort(): 通常O(n log n)(取决于JS引擎实现)
* join(): O(n)
**/
var isAnagram = function(s, t) {
if (s.length !== t.length) return false;
return s.split('').sort().join('') === t.split('').sort().join('');
};
// 下面是Deepseek给的优化版### (O(n)时间,O(1)空间)
var isAnagram = function(s, t) {
if (s.length !== t.length) return false;
const count = new Array(26).fill(0); // 因为题目说明只含小写字母
for (let i = 0; i < s.length; i++) {
count[s.charCodeAt(i) - 97]++; // 'a'.charCodeAt() = 97
count[t.charCodeAt(i) - 97]--;
}
return count.every(v => v === 0);
};
2.5 Cursor推荐使用哈希表计数法
- 这个我本来也不懂,但是仔细一看这个和2.3就是一种方法
- 就是用一个对象记载字符char和出现的次数的映射
- 我本来担心map[char]--;会不会为负数,我的担心是多余的,在他为负数之前,if (!map[char])就已经返回了
js
var isAnagram = function(s, t) {
if (s.length !== t.length) return false;
const map = {};
for (const char of s) {
map[char] = (map[char] || 0) + 1;
}
for (const char of t) {
if (!map[char]) return false;
map[char]--;
}
return true;
};
总结:
有志者,事竟成,破釜沉舟,百万秦关终属楚; 苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
虽然很简单的一个小算法题,但我也呕心沥血了,写完已经凌晨00:40了,望博友们给个支持!!!