Welcome to Subscribe On Youtube
3093. Longest Common Suffix Queries
Description
You are given two arrays of strings wordsContainer
and wordsQuery
.
For each wordsQuery[i]
, you need to find a string from wordsContainer
that has the longest common suffix with wordsQuery[i]
. If there are two or more strings in wordsContainer
that share the longest common suffix, find the string that is the smallest in length. If there are two or more such strings that have the same smallest length, find the one that occurred earlier in wordsContainer
.
Return an array of integers ans
, where ans[i]
is the index of the string in wordsContainer
that has the longest common suffix with wordsQuery[i]
.
Example 1:
Input: wordsContainer = ["abcd","bcd","xbcd"], wordsQuery = ["cd","bcd","xyz"]
Output: [1,1,1]
Explanation:
Let's look at each wordsQuery[i]
separately:
- For
wordsQuery[0] = "cd"
, strings fromwordsContainer
that share the longest common suffix"cd"
are at indices 0, 1, and 2. Among these, the answer is the string at index 1 because it has the shortest length of 3. - For
wordsQuery[1] = "bcd"
, strings fromwordsContainer
that share the longest common suffix"bcd"
are at indices 0, 1, and 2. Among these, the answer is the string at index 1 because it has the shortest length of 3. - For
wordsQuery[2] = "xyz"
, there is no string fromwordsContainer
that shares a common suffix. Hence the longest common suffix is""
, that is shared with strings at index 0, 1, and 2. Among these, the answer is the string at index 1 because it has the shortest length of 3.
Example 2:
Input: wordsContainer = ["abcdefgh","poiuygh","ghghgh"], wordsQuery = ["gh","acbfgh","acbfegh"]
Output: [2,0,2]
Explanation:
Let's look at each wordsQuery[i]
separately:
- For
wordsQuery[0] = "gh"
, strings fromwordsContainer
that share the longest common suffix"gh"
are at indices 0, 1, and 2. Among these, the answer is the string at index 2 because it has the shortest length of 6. - For
wordsQuery[1] = "acbfgh"
, only the string at index 0 shares the longest common suffix"fgh"
. Hence it is the answer, even though the string at index 2 is shorter. - For
wordsQuery[2] = "acbfegh"
, strings fromwordsContainer
that share the longest common suffix"gh"
are at indices 0, 1, and 2. Among these, the answer is the string at index 2 because it has the shortest length of 6.
Constraints:
1 <= wordsContainer.length, wordsQuery.length <= 104
1 <= wordsContainer[i].length <= 5 * 103
1 <= wordsQuery[i].length <= 5 * 103
wordsContainer[i]
consists only of lowercase English letters.wordsQuery[i]
consists only of lowercase English letters.- Sum of
wordsContainer[i].length
is at most5 * 105
. - Sum of
wordsQuery[i].length
is at most5 * 105
.
Solutions
Solution 1: Trie
The problem requires us to find the longest common suffix, so we can consider using a Trie.
We define the structure of the Trie node as follows:
children
: An array of length 26, used to store child nodes.length
: The length of the shortest string at the current node.idx
: The index of the string at the current node.
We traverse the string array wordsContainer
, and insert each string in reverse order into the Trie. During the insertion process, we update the length
and idx
of each node.
Next, we traverse the string array wordsQuery
. For each string, we search for the index of the longest common suffix string from the Trie. During the search process, if we encounter a null node, it means that there is no common suffix afterwards, and we can directly return the idx
of the current node.
The time complexity is $(L_1 \times |\Sigma| + L_2)$, and the space complexity is $O(L_1 \times |\Sigma|)$. Here, $L_1$ and $L_2$ are the sum of the lengths of the strings in wordsContainer
and wordsQuery
respectively; and $\Sigma$ is the size of the character set, in this problem $\Sigma = 26$.
-
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; } }
-
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; } };
-
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]
-
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 }
-
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; }