面试题 17.13. 恢复空格
难度:中等
题目描述
解题思路
1、简单动态规划 O(n2)
public int respace(String[] dictionary, String sentence) {
Set<String> dict = new HashSet<>(Arrays.asList(dictionary));
int n = sentence.length();
int[] dp = new int[n + 1];
for (int i = 1; i <= n; i++) {
dp[i] = dp[i-1] + 1;
for (int j = 0; j < i; j++) {
if(dict.contains(sentence.substring(j, i))) { //如果以j开头,以i结尾的字符在字典里
dp[i] = Math.min(dp[i], dp[j]);
}
}
}
return dp[n];
}
2、字典树优化
解法一最耗时的地方在于对于每个位置都要挨个遍历查找有没有以当前位置为结尾匹配上的字符。可以构建字典树来优化这个查找过程(抄题解)
class Solution {
public int respace(String[] dictionary, String sentence) {
// 构建字典树
Trie trie = new Trie();
for (String word: dictionary) {
trie.insert(word);
}
// 状态转移,dp[i] 表示字符串的前 i 个字符的最少未匹配数
int n = sentence.length();
int[] dp = new int[n + 1];
for (int i = 1; i <= n; i++) {
dp[i] = dp[i - 1] + 1;
for (int idx: trie.search(sentence, i - 1)) {
dp[i] = Math.min(dp[i], dp[idx]);
}
}
return dp[n];
}
}
class Trie {
TrieNode root;
public Trie() {
root = new TrieNode();
}
// 将单词倒序插入字典树
public void insert(String word) {
TrieNode cur = root;
for (int i = word.length() - 1; i >= 0; i--) {
int c = word.charAt(i) - 'a';
if (cur.children[c] == null) {
cur.children[c] = new TrieNode();
}
cur = cur.children[c];
}
cur.isWord = true;
}
// 找到 sentence 中以 endPos 为结尾的单词,返回这些单词的开头下标。
public List<Integer> search(String sentence, int endPos) {
List<Integer> indices = new ArrayList<>();
TrieNode cur = root;
for (int i = endPos; i >= 0; i--) {
int c = sentence.charAt(i) - 'a';
if (cur.children[c] == null) {
break;
}
cur = cur.children[c];
if (cur.isWord) {
indices.add(i);
}
}
return indices;
}
}
class TrieNode {
boolean isWord;
TrieNode[] children = new TrieNode[26];
public TrieNode() {}
}