跳转至

3093. 最长公共后缀查询

题目描述

给你两个字符串数组 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,将每个字符串倒序插入字典树中。在插入的过程中,我们更新每个节点的 lengthidx

接下来,我们遍历字符串数组 wordsQuery,对于每个字符串,我们从字典树中查找最长公共后缀的字符串下标,在寻找的过程中,如果遇到空节点,说明往后没有公共后缀了,我们可以直接返回当前节点的 idx

时间复杂度 $(L_1 \times |\Sigma| + L_2)$,空间复杂度 $O(L_1 \times |\Sigma|)$,其中 $L_1$ 和 $L_2$ 分别是 wordsContainerwordsQuery 的字符串长度之和;而 $\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;
}

评论