📅 2025-07-08|Day 34
第一章:静香的诗,胖虎也能写?
"你看!"静香一边甩着 ponytail,一边递过来两张纸:
ini
s = "anagram"
t = "nagaram"
"你敢信?胖虎说他是'原创'诗人,他写的 t 明明是把我 s 字母顺序调了个顺序!"
大雄挠头:"那也不能直接说是抄袭吧?字母顺序不一样,怎么说是'一样'的呢?"
哆啦A梦一口咬掉半个铜锣烧:"这就叫------字母异位词(Anagram) 。"
第二章:异位词到底是什么鬼?
"字母异位词"指的是:
✅ 两个字符串长度相等
✅ 每个字符出现的次数完全相同
✅ 顺序可以不同(谁先谁后无所谓)
哆啦A梦递给大雄一块"排序魔方":"你可以先试试排序法,简单粗暴。"
第三章:排序法 · 快速检测器上线!
csharp
function isAnagram(s: string, t: string): boolean {
if (s.length !== t.length ) return false;
let sArr: string[] = s.split('').sort();
let tArr: string[] = t.split('').sort();
return sArr.join('') === tArr.join('');
}
🧠 思路:排序后相等,就说明字母组成一模一样。
⏱ 时间复杂度:O(n log n);优点是写法清晰,缺点是排序浪费时间。
静香:
"不错,但如果我写了一首十万字的长诗,你还用 .sort()
,电脑得原地升天。"
第四章:双 Map 频次追踪术 · 我自己数!
哆啦A梦:
"那就靠你自己数数每个字母出现了几次!"
于是大雄写出双 Map 频率比较器(正是你写的那一版):
vbnet
function isAnagram(s: string, t: string): boolean {
if (s.length !== t.length) return false;
let sArr: string[] = s.split("");
let tArr: string[] = t.split("");
let sMap: Map<string, number> = new Map();
let tMap: Map<string, number> = new Map();
for (let i = 0; i < s.length; i++) {
// 更新 sMap
if (sMap.has(sArr[i])) {
let num = sMap.get(sArr[i])!;
sMap.set(sArr[i], num + 1);
} else {
sMap.set(sArr[i], 1);
}
// 更新 tMap
if (tMap.has(tArr[i])) {
let num = tMap.get(tArr[i])!;
tMap.set(tArr[i], num + 1);
} else {
tMap.set(tArr[i], 1);
}
}
// 合并 key,防止某个 map 缺 key
let aSet: string[] = Array.from(new Set([...sMap.keys(), ...tMap.keys()]));
for (let i = 0; i < aSet.length; i++) {
if (tMap.get(aSet[i]) !== sMap.get(aSet[i])) return false;
}
return true;
}
哆啦A梦点头:"你这次写得很好!不过可以再简洁点。"
第五章:简化!优化!干净利落!
大雄瞪大眼:"能更短?"
哆啦A梦:"当然!你完全可以把两个 Map 合成一个!"
✅ 优化版:一个 Map 解决战斗(频次差值法)
vbnet
function isAnagram(s: string, t: string): boolean {
if (s.length !== t.length) return false;
let map: Map<string, number> = new Map();
for (let i = 0; i < s.length; i++) {
map.set(s[i], (map.get(s[i]) || 0) + 1);
map.set(t[i], (map.get(t[i]) || 0) - 1);
}
for (let val of map.values()) {
if (val !== 0) return false;
}
return true;
}
🧠 思路:一个 Map 同时统计 s 的字符
+1
,t 的字符-1
,如果最后每个字母都为 0,说明字符频次相等!
第六章:终极版本(全 ASCII 字符)提升性能!
如果你知道字符串只包含小写字母(a-z),还可以用数组代替 Map,加速:
ini
function isAnagram(s: string, t: string): boolean {
if (s.length !== t.length) return false;
const freq: number[] = new Array(26).fill(0);
const aCode = 'a'.charCodeAt(0);
for (let i = 0; i < s.length; i++) {
freq[s.charCodeAt(i) - aCode]++;
freq[t.charCodeAt(i) - aCode]--;
}
return freq.every(count => count === 0);
}
🧪 测试一下吧!
ruby
isAnagram("anagram", "nagaram"); // ✅ true
isAnagram("rat", "car"); // ❌ false
isAnagram("aacc", "ccac"); // ❌ false
isAnagram("", ""); // ✅ true
📌 技术启示录:
解法 | 优点 | 缺点 | 复杂度 |
---|---|---|---|
排序法 | 简洁易写 | 时间略慢 O(n log n) | 时间+空间 |
双 Map | 通用、适合全字符集 | 写法稍冗 | O(n) |
差值 Map 优化 | 简洁高效,双向处理 | 要理解减法逻辑 | O(n) |
频次数组法 | 最快,空间固定 O(1) | 仅适用于小写字母 | O(n) |
🌌 终章:字符重组的算法之美
大雄仰望窗外星空:
"原来一个词和另一个词之间的关系,
并不在乎顺序,
而是------他们有没有一样的'灵魂'。"
哆啦A梦笑着补充:
"而算法,正是帮我们找出那些'灵魂是否一致'的工具。"