LeetCode 890中等数组 · 哈希
查找和替换模式 图解题解
这道题到底在问什么
给定单词列表 words 和模式串 pattern,返回所有与 pattern 匹配的单词。匹配 = 存在一个字母到字母的双射,把 pattern 的每个字母替换后正好得到该单词。
- 输入
- words=["abc","mee","aqq","ccc"], pattern="abb"
- 输出
- ["mee","aqq"]
- 输入
- 为什么 "ccc" 不行
- 输出
- pattern 的 a 和 b 都得映射成 c,两个字母撞到同一个,不是双射
最优解:一步一步想明白
- 3记住这套「一位一查、双向一致才放行」,下面每一帧都在套它。
- 4先看清模式 "abb":它的形状是「第一位单独,后两位相同」。任何匹配它的词,也必须是这个形状。逐个单词检验。
- 5换到单词 "abc"。两张映射表清空,准备一位一位地和模式 "abb" 对。
- 6第 0 位是新字母对:把「词 a → 式 a」记进 f,反过来「式 a → 词 a」记进 g。这一位绿了。
- 7第 1 位是新字母对:把「词 b → 式 b」记进 f,反过来「式 b → 词 b」记进 g。这一位绿了。
- 8第 2 位想把词 "c" 和式 "b" 配起来,可映射表里 式字母 "b" 早先已被词字母 "b" 占用,这一位却要它对应 "c"。撞车了,这个词出局。
- 9"abc" 在第 2 位就撞了车,没法构成双射。丢弃,不进答案。
- 10换到单词 "mee"。两张映射表清空,准备一位一位地和模式 "abb" 对。
- 11第 0 位是新字母对:把「词 m → 式 a」记进 f,反过来「式 a → 词 m」记进 g。这一位绿了。
- 12第 1 位是新字母对:把「词 e → 式 b」记进 f,反过来「式 b → 词 e」记进 g。这一位绿了。
- 13第 2 位的词 "e" 和式 "b" 在两张表里都已登记且正好对得上,一致通过,绿色推进到第 2 位。
- 14"mee" 从头到尾两张表都没冲突,说明能用一个双射把模式变成它。收进答案。
- 15换到单词 "aqq"。两张映射表清空,准备一位一位地和模式 "abb" 对。
- 16第 0 位是新字母对:把「词 a → 式 a」记进 f,反过来「式 a → 词 a」记进 g。这一位绿了。
- 17第 1 位是新字母对:把「词 q → 式 b」记进 f,反过来「式 b → 词 q」记进 g。这一位绿了。
- 18第 2 位的词 "q" 和式 "b" 在两张表里都已登记且正好对得上,一致通过,绿色推进到第 2 位。
- 19"aqq" 从头到尾两张表都没冲突,说明能用一个双射把模式变成它。收进答案。
- 20换到单词 "ccc"。两张映射表清空,准备一位一位地和模式 "abb" 对。
- 21第 0 位是新字母对:把「词 c → 式 a」记进 f,反过来「式 a → 词 c」记进 g。这一位绿了。
- 22第 1 位想把词 "c" 和式 "b" 配起来,可映射表里 词字母 "c" 早先已对应式字母 "a",这一位却要它对应 "b"。撞车了,这个词出局。
- 23"ccc" 在第 1 位就撞了车,没法构成双射。丢弃,不进答案。
- 24四个单词都对完了,能匹配模式 "abb" 的是 "mee" 和 "aqq"。这就是最终答案。
⚠️ 容易写错的地方
✗ 错:只建单向映射
✓ 对:必须双向都查
只查 词→式 会漏掉 "abc" 对 "abb"(多个词字母映到同一式字母),所以还需要 式→词 反查
✗ 错:冲突后还继续比后面的位
✓ 对:一发现冲突立即判这个词出局
前面已经矛盾,后面再对得上也救不回来,继续是浪费
✗ 错:把映射建反了还不一致
✓ 对:f 和 g 方向要固定且配套更新
两张表必须每次成对更新,漏一张就失去双向把关的意义
完整代码(Python / C++ / Java)
Python
from __future__ import annotations
from typing import *
from collections import *
from functools import *
from itertools import *
from math import *
from heapq import *
from bisect import *
class Solution:
def findAndReplacePattern(self, words: List[str], pattern: str) -> List[str]:
def match(s, t):
m1, m2 = [0] * 128, [0] * 128
for i, (a, b) in enumerate(zip(s, t), 1):
if m1[ord(a)] != m2[ord(b)]:
return False
m1[ord(a)] = m2[ord(b)] = i
return True
return [word for word in words if match(word, pattern)]C++
#include <algorithm>
#include <array>
#include <cctype>
#include <climits>
#include <cmath>
#include <deque>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <numeric>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <tuple>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
vector<string> findAndReplacePattern(vector<string>& words, string pattern) {
vector<string> ans;
auto match = [](string& s, string& t) {
int m1[128] = {0};
int m2[128] = {0};
for (int i = 0; i < s.size(); ++i) {
if (m1[s[i]] != m2[t[i]]) return 0;
m1[s[i]] = i + 1;
m2[t[i]] = i + 1;
}
return 1;
};
for (auto& word : words)
if (match(word, pattern)) ans.emplace_back(word);
return ans;
}
};Java
import java.util.*;
class Solution {
public List<String> findAndReplacePattern(String[] words, String pattern) {
List<String> ans = new ArrayList<>();
for (String word : words) {
if (match(word, pattern)) {
ans.add(word);
}
}
return ans;
}
private boolean match(String s, String t) {
int[] m1 = new int[128];
int[] m2 = new int[128];
for (int i = 0; i < s.length(); ++i) {
char c1 = s.charAt(i);
char c2 = t.charAt(i);
if (m1[c1] != m2[c2]) {
return false;
}
m1[c1] = i + 1;
m2[c2] = i + 1;
}
return true;
}
}复杂度
时间
O(n · L)
n 个单词,每个长 L,逐位扫一遍
空间
O(1)
映射只在小写字母上,至多 26 项,视为常数
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 查找和替换模式 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
能不能只用一张映射表?+
不行。只记 词→式 时,"abc" 对 "abb" 会被误判通过:词字母 b、c 都映到同一个式字母 b,词→式 单向表查不出「两个词字母挤到同一个式字母」。必须再加一张 式→词 反查,才能挡住撞车。
有没有不建两张表的等价写法?+
有。可以把每个单词和模式都「标准化」成同构编码,比如把每个字母换成它「首次出现的序号」(abb 变成 0,1,1),两边编码相等就匹配。或者像参考代码那样记录每个字母最近出现的位置并要求两边相等,本质都是在判同构。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 查找和替换模式 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。