|
68 | 68 |
|
69 | 69 | ## 解法
|
70 | 70 |
|
71 |
| -### 方法一 |
| 71 | +### 方法一:字典树 |
| 72 | + |
| 73 | +题目需要我们找到最长公共后缀,我们可以考虑使用字典树。 |
| 74 | + |
| 75 | +我们定义字典树的节点结构如下: |
| 76 | + |
| 77 | +- `children`:一个长度为 26 的数组,用于存储子节点。 |
| 78 | +- `length`:当前节点的最短字符串长度。 |
| 79 | +- `idx`:当前节点的字符串下标。 |
| 80 | + |
| 81 | +我们遍历字符串数组 `wordsContainer`,将每个字符串倒序插入字典树中。在插入的过程中,我们更新每个节点的 `length` 和 `idx`。 |
| 82 | + |
| 83 | +接下来,我们遍历字符串数组 `wordsQuery`,对于每个字符串,我们从字典树中查找最长公共后缀的字符串下标,在寻找的过程中,如果遇到空节点,说明往后没有公共后缀了,我们可以直接返回当前节点的 `idx`。 |
| 84 | + |
| 85 | +时间复杂度 $(L_1 \times |\Sigma| + L_2)$,空间复杂度 $O(L_1 \times |\Sigma|)$,其中 $L_1$ 和 $L_2$ 分别是 `wordsContainer` 和 `wordsQuery` 的字符串长度之和;而 $\Sigma$ 是字符集大小,本题中 $\Sigma = 26$。 |
72 | 86 |
|
73 | 87 | <!-- tabs:start -->
|
74 | 88 |
|
75 | 89 | ```python
|
76 | 90 | class Trie:
|
77 | 91 | __slots__ = ("children", "length", "idx")
|
78 | 92 |
|
79 |
| - def __init__(self, length=inf, idx=inf): |
| 93 | + def __init__(self): |
80 | 94 | self.children = [None] * 26
|
81 |
| - self.length = length |
82 |
| - self.idx = idx |
| 95 | + self.length = inf |
| 96 | + self.idx = inf |
83 | 97 |
|
84 | 98 | def insert(self, w: str, i: int):
|
85 | 99 | node = self
|
86 |
| - for c in w: |
| 100 | + if node.length > len(w): |
| 101 | + node.length = len(w) |
| 102 | + node.idx = i |
| 103 | + for c in w[::-1]: |
87 | 104 | idx = ord(c) - ord("a")
|
88 |
| - if not node.children[idx]: |
| 105 | + if node.children[idx] is None: |
89 | 106 | node.children[idx] = Trie()
|
90 | 107 | node = node.children[idx]
|
91 | 108 | if node.length > len(w):
|
92 | 109 | node.length = len(w)
|
93 | 110 | node.idx = i
|
94 |
| - elif node.length == len(w): |
95 |
| - node.idx = min(node.idx, i) |
96 | 111 |
|
97 |
| - def query(self, w: str): |
| 112 | + def query(self, w: str) -> int: |
98 | 113 | node = self
|
99 |
| - ans = node.idx |
100 |
| - for c in w: |
| 114 | + for c in w[::-1]: |
101 | 115 | idx = ord(c) - ord("a")
|
102 |
| - if not node.children[idx]: |
| 116 | + if node.children[idx] is None: |
103 | 117 | break
|
104 | 118 | node = node.children[idx]
|
105 |
| - ans = node.idx |
106 |
| - return ans |
| 119 | + return node.idx |
107 | 120 |
|
108 | 121 |
|
109 | 122 | class Solution:
|
110 | 123 | def stringIndices(
|
111 | 124 | self, wordsContainer: List[str], wordsQuery: List[str]
|
112 | 125 | ) -> List[int]:
|
113 |
| - k = 0 |
114 |
| - for i, w in enumerate(wordsContainer): |
115 |
| - if len(w) < len(wordsContainer[k]): |
116 |
| - k = i |
117 |
| - trie = Trie(len(wordsContainer[k]), k) |
| 126 | + trie = Trie() |
118 | 127 | for i, w in enumerate(wordsContainer):
|
119 |
| - trie.insert(w[::-1], i) |
120 |
| - ans = [] |
121 |
| - for i, w in enumerate(wordsQuery): |
122 |
| - ans.append(trie.query(w[::-1])) |
123 |
| - return ans |
| 128 | + trie.insert(w, i) |
| 129 | + return [trie.query(w) for w in wordsQuery] |
124 | 130 | ```
|
125 | 131 |
|
126 | 132 | ```java
|
127 |
| - |
| 133 | +class Trie { |
| 134 | + private final int inf = 1 << 30; |
| 135 | + private Trie[] children = new Trie[26]; |
| 136 | + private int length = inf; |
| 137 | + private int idx = inf; |
| 138 | + |
| 139 | + public void insert(String w, int i) { |
| 140 | + Trie node = this; |
| 141 | + if (node.length > w.length()) { |
| 142 | + node.length = w.length(); |
| 143 | + node.idx = i; |
| 144 | + } |
| 145 | + for (int k = w.length() - 1; k >= 0; --k) { |
| 146 | + int idx = w.charAt(k) - 'a'; |
| 147 | + if (node.children[idx] == null) { |
| 148 | + node.children[idx] = new Trie(); |
| 149 | + } |
| 150 | + node = node.children[idx]; |
| 151 | + if (node.length > w.length()) { |
| 152 | + node.length = w.length(); |
| 153 | + node.idx = i; |
| 154 | + } |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + public int query(String w) { |
| 159 | + Trie node = this; |
| 160 | + for (int k = w.length() - 1; k >= 0; --k) { |
| 161 | + int idx = w.charAt(k) - 'a'; |
| 162 | + if (node.children[idx] == null) { |
| 163 | + break; |
| 164 | + } |
| 165 | + node = node.children[idx]; |
| 166 | + } |
| 167 | + return node.idx; |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +class Solution { |
| 172 | + public int[] stringIndices(String[] wordsContainer, String[] wordsQuery) { |
| 173 | + Trie trie = new Trie(); |
| 174 | + for (int i = 0; i < wordsContainer.length; ++i) { |
| 175 | + trie.insert(wordsContainer[i], i); |
| 176 | + } |
| 177 | + int n = wordsQuery.length; |
| 178 | + int[] ans = new int[n]; |
| 179 | + for (int i = 0; i < n; ++i) { |
| 180 | + ans[i] = trie.query(wordsQuery[i]); |
| 181 | + } |
| 182 | + return ans; |
| 183 | + } |
| 184 | +} |
128 | 185 | ```
|
129 | 186 |
|
130 | 187 | ```cpp
|
131 |
| - |
| 188 | +class Trie { |
| 189 | +private: |
| 190 | + const int inf = 1 << 30; |
| 191 | + Trie* children[26]; |
| 192 | + int length = inf; |
| 193 | + int idx = inf; |
| 194 | + |
| 195 | +public: |
| 196 | + Trie() { |
| 197 | + for (int i = 0; i < 26; ++i) { |
| 198 | + children[i] = nullptr; |
| 199 | + } |
| 200 | + } |
| 201 | + |
| 202 | + void insert(string w, int i) { |
| 203 | + Trie* node = this; |
| 204 | + if (node->length > w.length()) { |
| 205 | + node->length = w.length(); |
| 206 | + node->idx = i; |
| 207 | + } |
| 208 | + for (int k = w.length() - 1; k >= 0; --k) { |
| 209 | + int idx = w[k] - 'a'; |
| 210 | + if (node->children[idx] == nullptr) { |
| 211 | + node->children[idx] = new Trie(); |
| 212 | + } |
| 213 | + node = node->children[idx]; |
| 214 | + if (node->length > w.length()) { |
| 215 | + node->length = w.length(); |
| 216 | + node->idx = i; |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + |
| 221 | + int query(string w) { |
| 222 | + Trie* node = this; |
| 223 | + for (int k = w.length() - 1; k >= 0; --k) { |
| 224 | + int idx = w[k] - 'a'; |
| 225 | + if (node->children[idx] == nullptr) { |
| 226 | + break; |
| 227 | + } |
| 228 | + node = node->children[idx]; |
| 229 | + } |
| 230 | + return node->idx; |
| 231 | + } |
| 232 | +}; |
| 233 | + |
| 234 | +class Solution { |
| 235 | +public: |
| 236 | + vector<int> stringIndices(vector<string>& wordsContainer, vector<string>& wordsQuery) { |
| 237 | + Trie* trie = new Trie(); |
| 238 | + for (int i = 0; i < wordsContainer.size(); ++i) { |
| 239 | + trie->insert(wordsContainer[i], i); |
| 240 | + } |
| 241 | + int n = wordsQuery.size(); |
| 242 | + vector<int> ans(n); |
| 243 | + for (int i = 0; i < n; ++i) { |
| 244 | + ans[i] = trie->query(wordsQuery[i]); |
| 245 | + } |
| 246 | + return ans; |
| 247 | + } |
| 248 | +}; |
132 | 249 | ```
|
133 | 250 |
|
134 | 251 | ```go
|
| 252 | +const inf = 1 << 30 |
| 253 | +
|
| 254 | +type Trie struct { |
| 255 | + children [26]*Trie |
| 256 | + length int |
| 257 | + idx int |
| 258 | +} |
| 259 | +
|
| 260 | +func newTrie() *Trie { |
| 261 | + return &Trie{length: inf, idx: inf} |
| 262 | +} |
| 263 | +
|
| 264 | +func (t *Trie) insert(w string, i int) { |
| 265 | + node := t |
| 266 | + if node.length > len(w) { |
| 267 | + node.length = len(w) |
| 268 | + node.idx = i |
| 269 | + } |
| 270 | + for k := len(w) - 1; k >= 0; k-- { |
| 271 | + idx := int(w[k] - 'a') |
| 272 | + if node.children[idx] == nil { |
| 273 | + node.children[idx] = newTrie() |
| 274 | + } |
| 275 | + node = node.children[idx] |
| 276 | + if node.length > len(w) { |
| 277 | + node.length = len(w) |
| 278 | + node.idx = i |
| 279 | + } |
| 280 | + } |
| 281 | +} |
| 282 | +
|
| 283 | +func (t *Trie) query(w string) int { |
| 284 | + node := t |
| 285 | + for k := len(w) - 1; k >= 0; k-- { |
| 286 | + idx := int(w[k] - 'a') |
| 287 | + if node.children[idx] == nil { |
| 288 | + break |
| 289 | + } |
| 290 | + node = node.children[idx] |
| 291 | + } |
| 292 | + return node.idx |
| 293 | +} |
| 294 | +
|
| 295 | +func stringIndices(wordsContainer []string, wordsQuery []string) (ans []int) { |
| 296 | + trie := newTrie() |
| 297 | + for i, w := range wordsContainer { |
| 298 | + trie.insert(w, i) |
| 299 | + } |
| 300 | + for _, w := range wordsQuery { |
| 301 | + ans = append(ans, trie.query(w)) |
| 302 | + } |
| 303 | + return |
| 304 | +} |
| 305 | +``` |
135 | 306 |
|
| 307 | +```ts |
| 308 | +class Trie { |
| 309 | + private children: Trie[] = new Array<Trie>(26); |
| 310 | + private length: number = Infinity; |
| 311 | + private idx: number = Infinity; |
| 312 | + |
| 313 | + public insert(w: string, i: number): void { |
| 314 | + let node: Trie = this; |
| 315 | + if (node.length > w.length) { |
| 316 | + node.length = w.length; |
| 317 | + node.idx = i; |
| 318 | + } |
| 319 | + for (let k: number = w.length - 1; k >= 0; --k) { |
| 320 | + let idx: number = w.charCodeAt(k) - 'a'.charCodeAt(0); |
| 321 | + if (node.children[idx] == null) { |
| 322 | + node.children[idx] = new Trie(); |
| 323 | + } |
| 324 | + node = node.children[idx]; |
| 325 | + if (node.length > w.length) { |
| 326 | + node.length = w.length; |
| 327 | + node.idx = i; |
| 328 | + } |
| 329 | + } |
| 330 | + } |
| 331 | + |
| 332 | + public query(w: string): number { |
| 333 | + let node: Trie = this; |
| 334 | + for (let k: number = w.length - 1; k >= 0; --k) { |
| 335 | + let idx: number = w.charCodeAt(k) - 'a'.charCodeAt(0); |
| 336 | + if (node.children[idx] == null) { |
| 337 | + break; |
| 338 | + } |
| 339 | + node = node.children[idx]; |
| 340 | + } |
| 341 | + return node.idx; |
| 342 | + } |
| 343 | +} |
| 344 | + |
| 345 | +function stringIndices(wordsContainer: string[], wordsQuery: string[]): number[] { |
| 346 | + const trie: Trie = new Trie(); |
| 347 | + for (let i: number = 0; i < wordsContainer.length; ++i) { |
| 348 | + trie.insert(wordsContainer[i], i); |
| 349 | + } |
| 350 | + const n: number = wordsQuery.length; |
| 351 | + const ans: number[] = new Array<number>(n); |
| 352 | + for (let i: number = 0; i < n; ++i) { |
| 353 | + ans[i] = trie.query(wordsQuery[i]); |
| 354 | + } |
| 355 | + return ans; |
| 356 | +} |
136 | 357 | ```
|
137 | 358 |
|
138 | 359 | <!-- tabs:end -->
|
|
0 commit comments