思路
-
精确匹配
- 用
HashSet
保存原始单词,查询时直接判断是否存在。
- 用
-
大小写忽略匹配
- 用
HashMap<String, String>
保存小写单词 -> 第一次出现的原始单词
。 - 用
putIfAbsent
,确保只记录第一次出现的单词。
- 用
-
元音模糊匹配
- 把单词里所有元音替换为
*
,得到模糊形式。 - 用
HashMap<String, String>
保存模糊形式 -> 第一次出现的原始单词
。 - 查询时同样先转小写再模糊化,然后查表。
- 把单词里所有元音替换为
查询优先级
- 精确匹配 → 大小写匹配 → 元音模糊匹配 → 否则返回
""
java
class Solution {
String vowels = "aeiou";
public String[] spellchecker(String[] wordlist, String[] queries) {
Set<String> wordSet = new HashSet<>(); // 精确匹配
Map<String, String> caseMap = new HashMap<>(); // 忽略大小写
Map<String, String> vowelMap = new HashMap<>(); // 忽略元音
// 预处理 wordlist
for (String word : wordlist) {
wordSet.add(word);
String lower = word.toLowerCase();
caseMap.putIfAbsent(lower, word);
String devoweled = devowel(lower);
vowelMap.putIfAbsent(devoweled, word);
}
String[] ans = new String[queries.length];
// 查询
for (int i = 0; i < queries.length; i++) {
String query = queries[i];
if (wordSet.contains(query)) { // 精确匹配
ans[i] = query;
} else {
String lower = query.toLowerCase();
if (caseMap.containsKey(lower)) { // 大小写匹配
ans[i] = caseMap.get(lower);
} else {
String devoweled = devowel(lower);
ans[i] = vowelMap.getOrDefault(devoweled, "");
}
}
}
return ans;
}
// 把所有元音替换成 '*'
private String devowel(String word) {
StringBuilder sb = new StringBuilder();
for (char c : word.toCharArray()) {
if (vowels.indexOf(c) != -1) {
sb.append('*');
} else {
sb.append(c);
}
}
return sb.toString();
}
}
方法二,用stream流
明白了,我帮你重整理,把 devowel 函数相关的语法(3,4,5)归为一个类,同时去掉 6,7。这样笔记更清晰、针对性更强。
java
import java.util.*;
import java.util.stream.*;
class Solution {
private final String vowels = "aeiou";
public String[] spellchecker(String[] wordlist, String[] queries) {
// 精确匹配
Set<String> wordSet = new HashSet<>(Arrays.asList(wordlist));
// 大小写忽略,保存第一次出现的原始单词
Map<String, String> caseMap = Arrays.stream(wordlist)
.collect(Collectors.toMap(
w -> w.toLowerCase(),
w -> w,
(oldVal, newVal) -> oldVal // 保证第一次出现
));
// 元音忽略,保存第一次出现的原始单词
Map<String, String> vowelMap = Arrays.stream(wordlist)
.collect(Collectors.toMap(
w -> devowel(w.toLowerCase()),
w -> w,
(oldVal, newVal) -> oldVal // 保证第一次出现
));
// 查询处理(用 stream 映射)
return Arrays.stream(queries)
.map(q -> {
if (wordSet.contains(q)) {
return q; // 精确匹配
}
String lower = q.toLowerCase();
if (caseMap.containsKey(lower)) {
return caseMap.get(lower); // 忽略大小写
}
return vowelMap.getOrDefault(devowel(lower), ""); // 忽略元音
})
.toArray(String[]::new);
}
// 把元音替换成 *
private String devowel(String word) {
return word.chars()
.mapToObj(c -> vowels.indexOf(c) != -1 ? "*" : String.valueOf((char) c))
.collect(Collectors.joining());
}
}
代码解析
1️⃣ 数组和 List 的区别
java
Set<String> wordSet = new HashSet<>(Arrays.asList(wordlist));
wordlist
是 数组 (String[]
)HashSet
构造函数需要 Collection 类型(List/Set 等),数组本身不是 CollectionArrays.asList(wordlist)
→ 把数组转成 List,方便初始化 Set
特性 | 数组 (String[] ) |
List (List<String> ) |
---|---|---|
大小 | 固定 | 可变 |
方法 | .length |
add, remove, contains 等 |
接口 | 不实现 Collection | 实现 Collection |
可作为构造参数 | 不可以 | 可以 |
2️⃣ Stream + Collectors.toMap(构建 Map)
java
Map<String, String> caseMap = Arrays.stream(wordlist)
.collect(Collectors.toMap(
w -> w.toLowerCase(), // key 映射规则
w -> w, // value 映射规则
(oldVal, newVal) -> oldVal // key 冲突时保留第一次出现
));
Arrays.stream(wordlist)
→ 把数组转成 StreamCollectors.toMap(keyMapper, valueMapper, mergeFunction)
参数 | 作用 |
---|---|
keyMapper |
Stream 元素如何生成 key (w -> w.toLowerCase() ) |
valueMapper |
Stream 元素如何生成 value (w -> w ) |
mergeFunction |
key 冲突时如何处理 (oldVal, newVal) -> oldVal → 保留第一次出现 |
示例:
java
wordlist = {"KiTe", "kite", "Hare"}
Map -> {"kite" -> "KiTe", "hare" -> "Hare"}
- 保证第一次出现的单词被记录,不被后续重复覆盖
3️⃣ devowel 函数解析(字符流 + 拼接)
java
private String devowel(String word) {
return word.chars()
.mapToObj(c -> vowels.indexOf(c) != -1 ? "*" : String.valueOf((char) c))
.collect(Collectors.joining());
}
3.1 word.chars()
- 将字符串拆成 IntStream
- 每个字符是 Unicode 编码的 int
- 示例:
java
"Hi".chars() -> 72, 105
3.2 String.valueOf((char) c)
- chars() 返回的是 int
(char) c
→ 把 int 转回字符String.valueOf(char)
→ 把字符变成长度为 1 的字符串- 用于最终拼接
3.3 Collectors.joining()
- 将 Stream 中的每个字符串 拼接成一个完整字符串
- 内部使用
StringBuilder
,不会像s += c
那样每次循环都生成新对象 - 高效拼接字符串
✅ 总结 devowel 技巧
word.chars()
→ 拆成字符流(char) c + String.valueOf()
→ 字符转换成字符串Collectors.joining()
→ 高效拼接