题目描述
给你两个字符串数组 wordsContainer
和 wordsQuery
。
对于每个 wordsQuery[i]
,你需要从 wordsContainer
中找到一个与 wordsQuery[i]
有 最长公共后缀 的字符串。如果 wordsContainer
中有两个或者更多字符串有最长公共后缀,那么答案为长度 最短 的。如果有超过两个字符串有 相同 最短长度,那么答案为它们在 wordsContainer
中出现 更早 的一个。
请你返回一个整数数组 ans
,其中 ans[i]
是 wordsContainer
中与 wordsQuery[i]
有 最长公共后缀 字符串的下标。
示例 1:
输入:wordsContainer = ["abcd","bcd","xbcd"], wordsQuery = ["cd","bcd","xyz"]
输出:[1,1,1]
解释:
我们分别来看每一个 wordsQuery[i]
:
- 对于
wordsQuery[0] = "cd"
,wordsContainer
中有最长公共后缀 "cd"
的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 1 的字符串,因为它的长度为 3 ,是最短的字符串。
- 对于
wordsQuery[1] = "bcd"
,wordsContainer
中有最长公共后缀 "bcd"
的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 1 的字符串,因为它的长度为 3 ,是最短的字符串。
- 对于
wordsQuery[2] = "xyz"
,wordsContainer
中没有字符串跟它有公共后缀,所以最长公共后缀为 ""
,下标为 0 ,1 和 2 的字符串都得到这一公共后缀。这些字符串中, 答案是下标为 1 的字符串,因为它的长度为 3 ,是最短的字符串。
示例 2:
输入:wordsContainer = ["abcdefgh","poiuygh","ghghgh"], wordsQuery = ["gh","acbfgh","acbfegh"]
输出:[2,0,2]
解释:
我们分别来看每一个 wordsQuery[i]
:
- 对于
wordsQuery[0] = "gh"
,wordsContainer
中有最长公共后缀 "gh"
的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 2 的字符串,因为它的长度为 6 ,是最短的字符串。
- 对于
wordsQuery[1] = "acbfgh"
,只有下标为 0 的字符串有最长公共后缀 "fgh"
。所以尽管下标为 2 的字符串是最短的字符串,但答案是 0 。
- 对于
wordsQuery[2] = "acbfegh"
,wordsContainer
中有最长公共后缀 "gh"
的字符串下标分别为 0 ,1 和 2 。这些字符串中,答案是下标为 2 的字符串,因为它的长度为 6 ,是最短的字符串。
提示:
1 <= wordsContainer.length, wordsQuery.length <= 104
1 <= wordsContainer[i].length <= 5 * 103
1 <= wordsQuery[i].length <= 5 * 103
wordsContainer[i]
只包含小写英文字母。
wordsQuery[i]
只包含小写英文字母。
wordsContainer[i].length
的和至多为 5 * 105
。
wordsQuery[i].length
的和至多为 5 * 105
。
解法
方法一:字典树
题目需要我们找到最长公共后缀,我们可以考虑使用字典树。
我们定义字典树的节点结构如下:
children
:一个长度为 26 的数组,用于存储子节点。
length
:当前节点的最短字符串长度。
idx
:当前节点的字符串下标。
我们遍历字符串数组 wordsContainer
,将每个字符串倒序插入字典树中。在插入的过程中,我们更新每个节点的 length
和 idx
。
接下来,我们遍历字符串数组 wordsQuery
,对于每个字符串,我们从字典树中查找最长公共后缀的字符串下标,在寻找的过程中,如果遇到空节点,说明往后没有公共后缀了,我们可以直接返回当前节点的 idx
。
时间复杂度 $(L_1 \times |\Sigma| + L_2)$,空间复杂度 $O(L_1 \times |\Sigma|)$,其中 $L_1$ 和 $L_2$ 分别是 wordsContainer
和 wordsQuery
的字符串长度之和;而 $\Sigma$ 是字符集大小,本题中 $\Sigma = 26$。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40 | class Trie:
__slots__ = ("children", "length", "idx")
def __init__(self):
self.children = [None] * 26
self.length = inf
self.idx = inf
def insert(self, w: str, i: int):
node = self
if node.length > len(w):
node.length = len(w)
node.idx = i
for c in w[::-1]:
idx = ord(c) - ord("a")
if node.children[idx] is None:
node.children[idx] = Trie()
node = node.children[idx]
if node.length > len(w):
node.length = len(w)
node.idx = i
def query(self, w: str) -> int:
node = self
for c in w[::-1]:
idx = ord(c) - ord("a")
if node.children[idx] is None:
break
node = node.children[idx]
return node.idx
class Solution:
def stringIndices(
self, wordsContainer: List[str], wordsQuery: List[str]
) -> List[int]:
trie = Trie()
for i, w in enumerate(wordsContainer):
trie.insert(w, i)
return [trie.query(w) for w in wordsQuery]
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 | class Trie {
private final int inf = 1 << 30;
private Trie[] children = new Trie[26];
private int length = inf;
private int idx = inf;
public void insert(String w, int i) {
Trie node = this;
if (node.length > w.length()) {
node.length = w.length();
node.idx = i;
}
for (int k = w.length() - 1; k >= 0; --k) {
int idx = w.charAt(k) - 'a';
if (node.children[idx] == null) {
node.children[idx] = new Trie();
}
node = node.children[idx];
if (node.length > w.length()) {
node.length = w.length();
node.idx = i;
}
}
}
public int query(String w) {
Trie node = this;
for (int k = w.length() - 1; k >= 0; --k) {
int idx = w.charAt(k) - 'a';
if (node.children[idx] == null) {
break;
}
node = node.children[idx];
}
return node.idx;
}
}
class Solution {
public int[] stringIndices(String[] wordsContainer, String[] wordsQuery) {
Trie trie = new Trie();
for (int i = 0; i < wordsContainer.length; ++i) {
trie.insert(wordsContainer[i], i);
}
int n = wordsQuery.length;
int[] ans = new int[n];
for (int i = 0; i < n; ++i) {
ans[i] = trie.query(wordsQuery[i]);
}
return ans;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 | class Trie {
private:
const int inf = 1 << 30;
Trie* children[26];
int length = inf;
int idx = inf;
public:
Trie() {
for (int i = 0; i < 26; ++i) {
children[i] = nullptr;
}
}
void insert(string w, int i) {
Trie* node = this;
if (node->length > w.length()) {
node->length = w.length();
node->idx = i;
}
for (int k = w.length() - 1; k >= 0; --k) {
int idx = w[k] - 'a';
if (node->children[idx] == nullptr) {
node->children[idx] = new Trie();
}
node = node->children[idx];
if (node->length > w.length()) {
node->length = w.length();
node->idx = i;
}
}
}
int query(string w) {
Trie* node = this;
for (int k = w.length() - 1; k >= 0; --k) {
int idx = w[k] - 'a';
if (node->children[idx] == nullptr) {
break;
}
node = node->children[idx];
}
return node->idx;
}
};
class Solution {
public:
vector<int> stringIndices(vector<string>& wordsContainer, vector<string>& wordsQuery) {
Trie* trie = new Trie();
for (int i = 0; i < wordsContainer.size(); ++i) {
trie->insert(wordsContainer[i], i);
}
int n = wordsQuery.size();
vector<int> ans(n);
for (int i = 0; i < n; ++i) {
ans[i] = trie->query(wordsQuery[i]);
}
return ans;
}
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53 | const inf = 1 << 30
type Trie struct {
children [26]*Trie
length int
idx int
}
func newTrie() *Trie {
return &Trie{length: inf, idx: inf}
}
func (t *Trie) insert(w string, i int) {
node := t
if node.length > len(w) {
node.length = len(w)
node.idx = i
}
for k := len(w) - 1; k >= 0; k-- {
idx := int(w[k] - 'a')
if node.children[idx] == nil {
node.children[idx] = newTrie()
}
node = node.children[idx]
if node.length > len(w) {
node.length = len(w)
node.idx = i
}
}
}
func (t *Trie) query(w string) int {
node := t
for k := len(w) - 1; k >= 0; k-- {
idx := int(w[k] - 'a')
if node.children[idx] == nil {
break
}
node = node.children[idx]
}
return node.idx
}
func stringIndices(wordsContainer []string, wordsQuery []string) (ans []int) {
trie := newTrie()
for i, w := range wordsContainer {
trie.insert(w, i)
}
for _, w := range wordsQuery {
ans = append(ans, trie.query(w))
}
return
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 | class Trie {
private children: Trie[] = new Array<Trie>(26);
private length: number = Infinity;
private idx: number = Infinity;
public insert(w: string, i: number): void {
let node: Trie = this;
if (node.length > w.length) {
node.length = w.length;
node.idx = i;
}
for (let k: number = w.length - 1; k >= 0; --k) {
let idx: number = w.charCodeAt(k) - 'a'.charCodeAt(0);
if (node.children[idx] == null) {
node.children[idx] = new Trie();
}
node = node.children[idx];
if (node.length > w.length) {
node.length = w.length;
node.idx = i;
}
}
}
public query(w: string): number {
let node: Trie = this;
for (let k: number = w.length - 1; k >= 0; --k) {
let idx: number = w.charCodeAt(k) - 'a'.charCodeAt(0);
if (node.children[idx] == null) {
break;
}
node = node.children[idx];
}
return node.idx;
}
}
function stringIndices(wordsContainer: string[], wordsQuery: string[]): number[] {
const trie: Trie = new Trie();
for (let i: number = 0; i < wordsContainer.length; ++i) {
trie.insert(wordsContainer[i], i);
}
const n: number = wordsQuery.length;
const ans: number[] = new Array<number>(n);
for (let i: number = 0; i < n; ++i) {
ans[i] = trie.query(wordsQuery[i]);
}
return ans;
}
|