题目
分析
方法一:前缀哈希【通过】
思路
遍历句子中每个单词,查看单词前缀是否为词根。
算法
将所有词根 roots
存储到集合 Set 中。遍历所有单词,判断其前缀是否为词根。如果是,则使用前缀代替该单词;否则不改变该单词。
class Solution {
public String replaceWords(List<String> roots, String sentence) {
Set<String> rootset = new HashSet();
for (String root: roots) rootset.add(root);
StringBuilder ans = new StringBuilder();
for (String word: sentence.split("\s+")) {
String prefix = "";
for (int i = 1; i <= word.length(); ++i) {
prefix = word.substring(0, i);
if (rootset.contains(prefix)) break;
}
if (ans.length() > 0) ans.append(" ");
ans.append(prefix);
}
return ans.toString();
}
}
复杂度分析
- 时间复杂度:,其中 $w_i $是第 i个单词的长度。检查第 i 个单词的所有前缀花费时间 。
- 空间复杂度:O(N),其中 N是句子的长度,词根使用
rootset
存储。
方法二:前缀树(数组)【通过】
思路和算法
把所有的词根放入前缀树中,在树上查找每个单词的最短词根,该操作可在线性时间内完成。
class Solution {
public String replaceWords(List<String> roots, String sentence) {
TrieNode trie = new TrieNode();
for (String root: roots) {
TrieNode cur = trie;
for (char letter: root.toCharArray()) {
if (cur.children[letter - 'a'] == null)
cur.children[letter - 'a'] = new TrieNode();
cur = cur.children[letter - 'a'];
}
cur.word = root;
}
StringBuilder ans = new StringBuilder();
for (String word: sentence.split("\s+")) {
if (ans.length() > 0)
ans.append(" ");
TrieNode cur = trie;
for (char letter: word.toCharArray()) {
if (cur.children[letter - 'a'] == null || cur.word != null)
break;
cur = cur.children[letter - 'a'];
}
ans.append(cur.word != null ? cur.word : word);
}
return ans.toString();
}
}
class TrieNode {
TrieNode[] children;
String word;
TrieNode() {
children = new TrieNode[26];
}
}
复杂度分析
- 时间复杂度:O(N),其中 NN 是
sentence
的长度。每次查询操作为线性时间复杂度。 - 空间复杂度:O(N),前缀树的大小。
方法三:前缀树(map)
//见到涉及单次前缀的 基本都是 用前缀树;
class Solution {
public String replaceWords(List<String> dict, String sentence) {
Trie trie = new Trie(dict,sentence); //穿件前缀树
for(int i=0;i<dict.size();i++){
trie.insert(dict.get(i),i+1); //将词根 放到前缀树
}
return trie.replace(); //执行替换
}
}
class TrieNode{
char c;
Map<Character,TrieNode> map = new HashMap<>();//这里我们用的是 map数据结构 而没有用数组,其实这两种都可以
int end;//定义当前的字符串 在列表中的位置 以1开始,为0说明这不是一个词;
public TrieNode(char c){
this.c = c;
}
}
class Trie{
TrieNode root;
String sentence;
List<String> list;
public Trie(List<String> list,String s){
root = new TrieNode('0');
this.sentence = s;
this.list = list;
}
//插入逻辑比较简单。基本一个模子方式。
public void insert(String word,int index){
TrieNode node = root;
for(int i=0;i<word.length();i++){
char cur = word.charAt(i);
if(!node.map.containsKey(cur)){
node.map.put(cur,new TrieNode(cur));
}
node = node.map.get(cur);
}
node.end = index;
}
public String search(String key){
TrieNode node = root;
for(int i=0;i<key.length();i++){
char cur = key.charAt(i);
if(node.end>0){ //如果 end大于0 说明 是 一个最短的完整词根 直接返回
return list.get(node.end-1);
}else{
if(!node.map.containsKey(cur)){ //如果 没有 则 直接完整返回
return key;
}else{
node = node.map.get(cur);
}
}
}
return key;
}
public String replace(){
String[] ss = this.sentence.split(" ");
StringBuilder sb = new StringBuilder();
for(String s:ss){
sb.append(search(s)).append(" ");
}
return sb.substring(0,sb.length()-1);
}
}