题目地址:
https://leetcode.com/problems/replace-words/
给定一个字符串数组,再给定一个英文句子,句子里只含英文单词,并且以空格分隔。如果句子中的某个单词以数组中某个字符串为前缀,则将其替换为这个前缀。存在多个字符串作为其前缀,则取最短的那个。返回替换完后的英文句子。
思路是用Trie,将数组中所有字符串存进一个Trie中,然后将英文句子分割成单词,对每个单词寻找Trie中的最短前缀即可。代码如下:
import java.util.List;
public class Solution {
class Trie {
class Node {
boolean isWord;
Node[] next;
Node() {
next = new Node[26];
}
}
Node root;
Trie(List<String> dict) {
root = new Node();
for (String word : dict) {
insert(word);
}
}
// 这里的insert的时候,如果发现走到了单词末尾就直接退出,不需要继续添加了,
// 原因是题目只要求找最短前缀,如果两个字符串一个是另一个的前缀,那只需要存短的那个就行了
private void insert(String word) {
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (cur.next[c - 'a'] == null) {
cur.next[c - 'a'] = new Node();
}
cur = cur.next[c - 'a'];
// 遇到单词末尾了直接退出
if (cur.isWord) {
return;
}
}
cur.isWord = true;
}
// 在Trie里寻找word的最短前缀,并这个最短前缀返回;若不存在则返回空串
private String prefixOf(String word) {
StringBuilder sb = new StringBuilder();
Node cur = root;
for (int i = 0; i < word.length(); i++) {
char c = word.charAt(i);
if (cur.next[c - 'a'] != null) {
// 每走一步就append路径上的字符
cur = cur.next[c - 'a'];
sb.append(c);
} else {
// 走到null说明Trie里不存在word的前缀,直接跳出返回空串
break;
}
// 如果第一次发现了单词末尾,那么就找到了最短前缀,直接返回前缀
if (cur.isWord) {
return sb.toString();
}
}
return "";
}
}
public String replaceWords(List<String> dict, String sentence) {
Trie trie = new Trie(dict);
String[] words = sentence.split(" ");
for (int i = 0; i < words.length; i++) {
String word = words[i];
// 找word的在trie中的最短前缀
String pref = trie.prefixOf(word);
// 如果pref为空,那说明不存在前缀,跳过该单词;否则进行替换
if (!pref.isEmpty()) {
words[i] = pref;
}
}
// 最后以空格join回来
return String.join(" ", words);
}
}
时空复杂度 O ( n ) O(n) O(n), n n n为句子的长度。