题目描述
题目分析
字母树
动态规划组成部分一:确定状态
动态规划组成部分三:初始条件和边界情况
动态规划组成部分四:计算顺序
Java代码实现
char[] tar = null;
int K = 0;
int n = 0;
List<String> res = null;
public List<String> kDistance(String[] words, String target, int k) {
tar = target.toCharArray();
n = tar.length;
K = k;
res = new ArrayList<>();
TrieNode root = new TrieNode();
for (int i = 0; i < words.length; i++)
root.insert(words[i]);
int[] f = new int[n + 1];
for (int i = 0; i <= n; i++)
f[i] = i;
dfs(root, f);
return res;
}
private void dfs(TrieNode p, int[] f) {
int[] nf = new int[n + 1];
if (p.hasWord)
if (f[n] <= K)
res.add(p.word);
for (int i = 0; i < 26; i++) {
if (p.sons[i] == null)
continue;
nf[0] = f[0] + 1;
for (int j = 1; j <= n; j++) {
nf[j] = Math.min(Math.min(nf[j - 1] + 1, f[j] + 1), f[j - 1] + 1);
if (i == tar[j - 1] - 'a')
nf[j] = Math.min(nf[j], f[j - 1]);
}
dfs(p.sons[i], nf);
}
}
}
class TrieNode {
TrieNode[] sons;
boolean hasWord;
String word;
public TrieNode() {
sons = new TrieNode[26];
for (int i = 0; i < 26; i++)
sons[i] = null;
hasWord = false;
}
public void insert(String wordStr) {
char[] word = wordStr.toCharArray();
int c;
TrieNode p = this;
for (int i = 0; i < word.length; i++) {
c = word[i] - 'a';
if (p.sons[c] == null)
p.sons[c] = new TrieNode();
p = p.sons[c];
}
p.hasWord = true;
p.word = wordStr;
}
}
代码解读
这个题目还是非常难的,这里对Java代码进行解读。
首先是声明了四个全局变量,之所以声明为全局变量是因为这是dfs每次进行搜索的时候需要用到的,如果将它们作为参数传入到dfs的 参数列表中则会使得dfs函数变得冗杂,所以最佳选择就是声明为全局变量。
然后是字典树TrieNode的定义,它的三个成员分为是TrieNode[] sons,boolean hasWord和String word,sons表示节点的孩子节点;hasWord则表示是不是给出的字符串,如果是true就表示该节点及祖先节点组成了一个待求的字符串,word就表示该字符串。insert函数则是用来将所有的待求字符串添加到root节点中。
最后说一下dfs函数,dfs函数有两个参数,TrieNode p和 int[] f,p即表示要深度优先搜索的当前节点,f则表示当前节点的前缀字符串(包括当前节点所代表的字符)与target字符串的编辑距离,如f[n]表示当前节点的前缀字符串与 target的前n个字符的编辑距离。
之所以要将数组f传入dfs函数参数列表,是因为这个数组f是在随着节点变化的,每个节点与target的编辑距离是不同的,而且在进行当前节点的f数组计算的时候需要用到父亲节点的f数组,所以要将f数组写的到参数列表传递给子节点使用。
nf[j] = Math.min(Math.min(nf[j - 1] + 1, f[j] + 1), f[j - 1] + 1);分别对应着编辑距离的增、删和改三种情况,画张图理解起来比较直观
新增是在当前已经加入了“a”的基础上新增,所以需要使用nf数组,即nf[j-1]+1。
删除了“a”之后,字符串与前缀字符出一样,所以使用的是f数组,即f[j]+1。
将“a”修改为“b”后,相当于将target字符串的最后一个字符“b”抵消掉,当前节点的“b”也被抵消掉,变成了前缀节点字符串,所以使用的是f数组,即f[j-1]+1。