开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情
一 描述
给定字符串 s
和字符串数组 words
, 返回 words[i]
中是s
的子序列的单词个数 。
字符串的 子序列 是从原始字符串中生成的新字符串,可以从中删去一些字符(可以是none),而不改变其余字符的相对顺序。
例如, “ace”
是 “abcde”
的子序列。
示例 1:
输入: s = "abcde", words = ["a","bb","acd","ace"]
输出: 3
解释: 有三个是 s 的子序列的单词: "a", "acd", "ace"。
Example 2:
输入: s = "dsahjpjauf", words = ["ahjpjau","ja","ahbwzgqnuk","tnmlanowax"]
输出: 2
提示:
- 1 <= s.length <= 5 * 10^4
- 1 <= words.length <= 5000
- 1 <= words[i].length <= 50
- words[i]和 s 都只由小写字母组成。
二 分析
字典树构建完成后,深度优选遍历字典树
判断结点所代表的字符,是否出现在后续字符串中
以测试用例:"abcde",["a","bb","acd","ace"]为例:
首次进入方法时,结点为root,字符串的起始位置为0
root结点的e为0,不加入结果
root的后续结点有a、b两个结点
a在字符串中存在,且下标为0。
递归调用search方法,结点为a,字符串的起始位置为1(0+1)
a结点的e为1,将1加入结果
a的后续结点有c结点
此时判断c在字符串中是否存在时,需要从a的后面开始查找,
这就是传入的起始位置的作用
三 答案
class Solution {
public int numMatchingSubseq(String s, String[] words) {
Trie trie = new Trie();
for (String word : words) {
trie.insert(word);
}
return trie.search(s);
}
class Node {
int e;
Node[] nexts = new Node[26];
}
class Trie {
Node root;
public Trie() {
root = new Node();
}
public void insert(String word) {
Node cur = root;
int index;
for (char c : word.toCharArray()) {
index = c - 'a';
if (cur.nexts[index] == null) {
cur.nexts[index] = new Node();
}
cur = cur.nexts[index];
}
cur.e++;
}
int result;
public int search(String word) {
search(word, 0, root);
return result;
}
public void search(String word, int index, Node node) {
if (node.e > 0) {
result += node.e;
}
Node next;
int indexOf;
for (int i = 0; i < node.nexts.length; i++) {
next = node.nexts[i];
if (next != null) {
indexOf = word.indexOf(i + 'a', index);
if (indexOf != -1) {
search(word, indexOf + 1, next);
}
}
}
}
}
}