小哆啦刷力扣第33天
给定一个 pattern 和一个字符串 s,判断 s 是否"遵循"这个规律 ------ 简单说,就是字母和单词之间存在"一一绑定关系",谁是谁的谁,不能乱认亲。
第一章:谁在背诵台词剧本?
"大雄!"
"你记得那部《狗狗的独白》电影吗?不是说台词 abba
代表 dog cat cat dog
吗?"
"我觉得有人改剧本了!"静香拿着一份神秘演出稿吼道:"abba → dog cat cat fish
?!这鱼是来跑龙套的吗?"
哆啦A梦正好端着铜锣烧走过,一听就懂:"这是经典的模式匹配问题!"
大雄抓起键盘,打开力扣,敲出第一版剧本审核器:
arduino
function wordPattern(pattern: string, s: string): boolean {
const words = s.split(' ');
if (pattern.length !== words.length) return false;
const map = new Map<string, string>();
for (let i = 0; i < pattern.length; i++) {
const char = pattern[i];
const word = words[i];
if (map.has(char)) {
if (map.get(char) !== word) return false;
} else {
map.set(char, word);
}
}
return true;
}
运行 "abba" vs "dog cat cat fish"
竟然返回 true
!
哆啦A梦皱起了眉头:"大雄啊......你又只问了字母的想法,却没问问单词愿不愿意。"
第二章:双向绑定协议 · Part II
"映射是双向的。"哆啦A梦放下铜锣烧,冷静地分析。
数学意义上,这要求构建一个"双射函数(bijection) ":
- 每个字母必须唯一对应一个单词(injective)
- 每个单词也只能属于一个字母(surjective)
大雄灵光一闪:"那我再开一个 Map,反过来记录单词对应的字母!"
于是出现了进化版代码:
typescript
function wordPattern(pattern: string, s: string): boolean {
const words = s.trim().split(/\s+/);
if (pattern.length !== words.length) return false;
const p2w = new Map<string, string>();
const w2p = new Map<string, string>();
for (let i = 0; i < pattern.length; i++) {
const p = pattern[i], w = words[i];
if ((p2w.has(p) && p2w.get(p) !== w) ||
(w2p.has(w) && w2p.get(w) !== p)) {
return false;
}
p2w.set(p, w);
w2p.set(w, p);
}
return true;
}
"这下不只是 pattern 在挑选 word,word 也能反审 pattern,恋爱自由,双向同意。"
第三章:测试现场 · 映射速查表
pattern |
s |
结果 | 原因说明 |
---|---|---|---|
"abba" |
"dog cat cat dog" |
✅ true | a→dog ,b→cat 成立 |
"abba" |
"dog cat cat fish" |
❌ false | a→dog ,b→cat vs a→fish 冲突 |
"aaaa" |
"dog dog dog dog" |
✅ true | 所有人都爱 dog |
"abba" |
"dog dog dog dog" |
❌ false | a→dog ,但 b 也想要 dog,重婚 |
"abc" |
"dog cat" |
❌ false | pattern 和 s 长度不一致 |
第四章:映射错乱の真实世界悲剧
胖虎举手发问:"那如果我把 pattern 改成 "abcabc"
,s 改成 "one two three one two three"
呢?"
"可以!"大雄说,"每个字符绑定唯一的单词,每次都按顺序来,谁也不偷情。"
胖虎又说:"那我把 s 变成 "one two one one two one"
,pattern 还是 "abcabc"
?"
啪------测试结果是:false!
"a 本来绑定的是 one,b 是 two,但你现在让 b 也说 one,简直是角色乱窜现场演剧!"哆啦一巴掌拍醒胖虎。
第五章:深入字符词汇映射机制
🧠 底层原理拆解:
- 字符映射本质上是一个键值映射表
Map<char, string>
; - 字符个数 ≠ 单词个数 ➜ 提前返回 false,减少无谓计算;
- Set 无法满足双向性要求 ,所以用两个
Map
更明确; - 若使用 JavaScript 对象
{}
,需要额外考虑键名冲突问题(如__proto__
),推荐使用Map
;
第六章:映射是契约,不是猜测
"这道题,考察的不是你的字符串操作能力。"哆啦A梦闭上双眼道:
"它考察的是你是否真的理解'映射'这两个字的含义。"
映射是双向约定,是数学中的忠诚,是程序中的规则,是数据结构中的婚姻法。
📌 技术启示录:
关键词 | 含义 |
---|---|
双射(bijection) | 一一对应,映射双向唯一 |
Map | 键值映射,适合字符与词之间的配对 |
Split + Trim | 清洗输入,防止被多个空格或换行符破坏测试用例 |
预检优化 | 长度不等立即返回 false,避免陷入无效循环 |
📦 bonus:泛型映射工具(支持 Emoji、非ASCII 等复杂映射)
typescript
function isBijectiveMapping<T extends string, U extends string>(
source: T[],
target: U[]
): boolean {
if (source.length !== target.length) return false;
const map1 = new Map<T, U>();
const map2 = new Map<U, T>();
for (let i = 0; i < source.length; i++) {
const s = source[i], t = target[i];
if ((map1.has(s) && map1.get(s) !== t) ||
(map2.has(t) && map2.get(t) !== s)) {
return false;
}
map1.set(s, t);
map2.set(t, s);
}
return true;
}
"连外星文字都能绑定!"大雄惊呼,"我终于能匹配 🐶🐱🐸🐶 和 🍞🥓🥓🍞 了!"
🌌 终章:算法は秩序の约定
当所有测试通过,屏幕上浮现出大大的 "Accepted" 时,哆啦A梦放下代码本,感慨道:
"真正的规则,不是为了限制,而是为了彼此承诺。"
窗外星空如海,铜锣烧的香气飘进夜色。大雄低头看着 pattern,轻轻说:
"原来每一段字符,都是语言在试图写出内心的结构。"
而胖虎远处怒吼:"你们到底在写算法还是谈恋爱啊?!🐟 也值得被爱好吧?!"