Skip to content

Commit 0f08274

Browse files
committed
✨ feat: refactor trie
1 parent 43b1c19 commit 0f08274

File tree

11 files changed

+796
-369
lines changed

11 files changed

+796
-369
lines changed

Diff for: MIGRATION.md

+9
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,15 @@ No breaking changes.
295295
5. `init()` is renamed to `reset()`, and the parameters is changed to object style.
296296

297297

298+
### @algorithm.ts/trie
299+
300+
1. `.insert()` is renamed to `.set()`.
301+
2. `.math()` is renamed to `.get()`.
302+
3. `.hasPrefixMatched` is renamed to `.hasPrefix()`.
303+
4. `.init()` is removed, use `.clear()` to initialize trie.
304+
305+
306+
298307
### @algorithm.ts/upper-bound
299308

300309
1. This package is removed, use `@algorithm.ts/binary-search` instead.

Diff for: packages/trie/README.md

+86-76
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<header>
22
<h1 align="center">
3-
<a href="https://github.com/guanghechen/algorithm.ts/tree/release-2.x.x/packages/trie#readme">@algorithm.ts/trie</a>
3+
<a href="https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x/packages/trie#readme">@algorithm.ts/trie</a>
44
</h1>
55
<div align="center">
66
<a href="https://www.npmjs.com/package/@algorithm.ts/trie">
@@ -54,13 +54,11 @@ A typescript implementation of the **TRIE** data structure.
5454

5555
The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki/Trie):
5656

57-
> In computer science, a trie, also called digital tree or prefix tree, is a
58-
> type of search tree, a tree data structure used for locating specific keys
59-
> from within a set. These keys are most often strings, with links between
60-
> nodes defined not by the entire key, but by individual characters. In order
61-
> to access a key (to recover its value, change it, or remove it), the trie
62-
> is traversed depth-first, following the links between nodes, which
63-
> represent each character in the key.
57+
> In computer science, a trie, also called digital tree or prefix tree, is a type of search tree, a
58+
> tree data structure used for locating specific keys from within a set. These keys are most often
59+
> strings, with links between nodes defined not by the entire key, but by individual characters. In
60+
> order to access a key (to recover its value, change it, or remove it), the trie is traversed
61+
> depth-first, following the links between nodes, which represent each character in the key.
6462
6563

6664
## Install
@@ -77,68 +75,66 @@ The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki
7775
yarn add @algorithm.ts/trie
7876
```
7977

80-
* deno
81-
82-
```typescript
83-
import { createTrie } from 'https://raw.githubusercontent.com/guanghechen/algorithm.ts/main/packages/trie/src/index.ts'
84-
```
8578

8679
## Usage
8780

88-
* Trie
81+
* Trie: Trie implements the [Collection][] interface.
8982

90-
- `init(): void`: Initialize a trie.
83+
- `set(element: Readonly<E>, value: V, start?: number, end?: number): this`:
84+
Insert a string (or an array) into the trie.
9185

92-
- `insert(str: string, v: T, start?: number, end?: number): void`: Insert a
93-
string into the trie.
86+
- `delete(element: Readonly<E>, start?: number, end?: number): boolean`:
87+
Remove a string (or an array) from the trie.
9488

95-
- `match(str: string, start?: number, end?: number): T | null`: Find a word in
96-
the trie which exact match the `str.slice(start, end)`. If there is such a
97-
word, return its additional value, otherwise return null.
89+
- `get(element: Readonly<E>, start?: number, end?: number): V | undefined`:
90+
Find the value of the element which exactly matched the `element.slice(start, end)`.
91+
If there is no such an element, then return undefined.
9892

99-
- `hasPrefixMatched(str: string, start?: number, end?: number): boolean`:
100-
Check if there is a word `w` in the trie satisfied that
101-
`w.slice(0, end - start)` equals to `str.slice(start, end)`.
93+
- `has(element: Readonly<E>, start?: number, end?: number): boolean`:
94+
Check if there is an element exactly matched the `element.slice(start, end)`.
10295

103-
- `find(str: string, start?: number, end?: number): TrieNodeData<T> | null`:
96+
- `hasPrefix(prefix: Readonly<E>, start?: number, end?: number): boolean`:
97+
Check if there is an element which prefix matched the `prefix.slice(start, end)`.
98+
99+
- `find(element: Readonly<E>, start?: number, end?: number): ITrieNodeData<V> | undefined`:
104100
Find word with smallest length in the trie which exact match the
105-
`str.slice(start, x)`, where the x is an integer in the range [start, _end).
101+
`element.slice(start, x)`, where the x is an integer in the range [start, _end).
106102

107-
- `findAll(str: string, start?: number, end?: number): Array<TrieNodeData<T>>`:
103+
- `findAll(element: Readonly<E>, start?: number, end?: number): Iterable<ITrieNodeData<V>>`:
108104
Find all words in the trie which exact match the
109-
`str.slice(start, x)`, where the x is an integer in the range [start, _end).
105+
`element.slice(start, x)`, where the x is an integer in the range [start, _end).
110106

111107
* Util
112108

113-
- `lowercaseIdx(c: string): number`: Calc idx of lowercase English letter.
114-
- `uppercaseIdx(c: string): number`: Calc idx of uppercase English letter.
115109
- `digitIdx(c: string): number`: Calc idx of digit character.
110+
- `uppercaseIdx(c: string): number`: Calc idx of uppercase English letter.
111+
- `lowercaseIdx(c: string): number`: Calc idx of lowercase English letter.
112+
- `alphaNumericIdx(c: string): number`: Calc idx of digit, lowercase/uppercase English leter.
116113

117114
### Example
118115

119116
* A solution of https://leetcode.com/problems/word-break-ii/:
120117

121118
```typescript
122119
import type { ITrie } from '@algorithm.ts/trie'
123-
import { createTrie, lowercaseIdx } from '@algorithm.ts/trie'
120+
import { Trie, lowercaseIdx } from '@algorithm.ts/trie'
124121

125-
export function wordBreak(s: string, wordDict: string[]): string[] {
126-
if (s.length <= 0) return []
122+
const trie: ITrie<string, number> = new Trie<string, number>({
123+
SIGMA_SIZE: 26,
124+
idx: lowercaseIdx,
125+
mergeNodeValue: (_x, y) => y,
126+
})
127127

128-
const trie: ITrie = createTrie({
129-
SIGMA_SIZE: 26,
130-
ZERO: 0,
131-
idx: lowercaseIdx,
132-
mergeAdditionalValues: (x, y) => y,
133-
})
128+
export function wordBreak(text: string, wordDict: string[]): string[] {
129+
if (text.length <= 0) return []
134130

135-
trie.init()
131+
trie.clear()
136132
for (let i = 0; i < wordDict.length; ++i) {
137133
const word = wordDict[i]
138-
trie.insert(word, i + 1)
134+
trie.set(word, i)
139135
}
140136

141-
const N = s.length
137+
const N = text.length
142138
const results: string[] = []
143139
const collect: number[] = []
144140
dfs(0, 0)
@@ -149,14 +145,13 @@ The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki
149145
results.push(
150146
collect
151147
.slice(0, cur)
152-
.map(x => wordDict[x - 1])
148+
.map(x => wordDict[x])
153149
.join(' '),
154150
)
155151
return
156152
}
157153

158-
const pairs = trie.findAll(s, pos)
159-
for (const { end, val } of pairs) {
154+
for (const { end, val } of trie.findAll(text, pos, text.length)) {
160155
collect[cur] = val
161156
dfs(cur + 1, end)
162157
}
@@ -167,58 +162,72 @@ The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki
167162
* A solution of https://leetcode.com/problems/word-search-ii/
168163

169164
```typescript
170-
import { Trie, createTrie, lowercaseIdx } from '@algorithm.ts/trie'
165+
import { Trie, lowercaseIdx } from '@algorithm.ts/trie'
171166

172-
function findWords(board: string[][], words: string[]): string[] {
173-
if (words.length === 0) return []
167+
class CustomTrie<E extends unknown[] | string, V> extends Trie<E, V> {
168+
public getSnapshot(): { ch: Uint32Array[]; values: Array<V | undefined> } {
169+
return {
170+
ch: this._ch,
171+
values: this._values,
172+
}
173+
}
174+
}
174175

175-
const R = board.length
176-
if (R <= 0) return []
176+
const trie = new CustomTrie<string, number>({
177+
SIGMA_SIZE: 26,
178+
idx: lowercaseIdx,
179+
mergeNodeValue: (x, _y) => x,
180+
})
177181

182+
export function findWords(board: string[][], words: string[]): string[] {
183+
const N = words.length
184+
const R = board.length
178185
const C = board[0].length
179-
if (C <= 0) return []
186+
if (N <= 0 || R <= 0 || C <= 0) return []
180187

181-
const trie: Trie = createTrie({
182-
SIGMA_SIZE: 26,
183-
ZERO: 0,
184-
idx: lowercaseIdx,
185-
mergeAdditionalValues: x => x,
186-
})
188+
trie.clear()
189+
for (let i = 0; i < N; ++i) trie.set(words[i], i)
187190

188-
trie.init()
191+
const boardCode: number[][] = []
192+
for (let r = 0; r < R; ++r) {
193+
const codes: number[] = []
194+
for (let c = 0; c < C; ++c) codes[c] = board[r][c].charCodeAt(0) - 97
195+
boardCode[r] = codes
196+
}
189197

190198
const visited: boolean[][] = new Array(R)
191199
for (let r = 0; r < R; ++r) visited[r] = new Array(C).fill(false)
192200

193-
const boardWord: string[] = []
201+
let matchedWordCount = 0
202+
const isWordMatched: boolean[] = new Array(N).fill(false)
203+
204+
const { ch, values } = trie.getSnapshot()
194205
for (let r = 0; r < R; ++r) {
195206
for (let c = 0; c < C; ++c) {
196207
dfs(0, r, c)
197208
}
198209
}
199210

200-
const results: string[] = []
201-
for (const word of words) {
202-
if (trie.hasPrefixMatched(word)) results.push(word)
203-
}
211+
const results: string[] = words.filter((_w, i) => isWordMatched[i])
204212
return results
205213

206-
function dfs(cur: number, r: number, c: number): void {
207-
if (cur === 10 || r < 0 || r >= R || c < 0 || c >= C) {
208-
trie.insert(boardWord.join(''), 1, 0, cur)
209-
return
210-
}
214+
function dfs(u: number, r: number, c: number): void {
215+
if (visited[r][c] || matchedWordCount === N) return
211216

212-
if (visited[r][c]) return
217+
const u2: number = ch[u][boardCode[r][c]]
218+
if (u2 === 0) return
213219

214-
visited[r][c] = true
215-
boardWord[cur] = board[r][c]
220+
const val = values[u2]
221+
if (val !== undefined && !isWordMatched[val]) {
222+
isWordMatched[val] = true
223+
matchedWordCount += 1
224+
}
216225

217-
const nextCur: number = cur + 1
218-
dfs(nextCur, r - 1, c)
219-
dfs(nextCur, r, c + 1)
220-
dfs(nextCur, r + 1, c)
221-
dfs(nextCur, r, c - 1)
226+
visited[r][c] = true
227+
if (r > 0) dfs(u2, r - 1, c)
228+
if (c + 1 < C) dfs(u2, r, c + 1)
229+
if (r + 1 < R) dfs(u2, r + 1, c)
230+
if (c > 0) dfs(u2, r, c - 1)
222231
visited[r][c] = false
223232
}
224233
}
@@ -229,4 +238,5 @@ The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki
229238
* https://en.wikipedia.org/wiki/Trie
230239

231240

232-
[homepage]: https://github.com/guanghechen/algorithm.ts/tree/release-2.x.x/packages/trie#readme
241+
[homepage]: https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x/packages/trie#readme
242+
[Collection]: https://github.com/guanghechen/algorithm.ts/tree/release-3.x.x/packages/types#readme

Diff for: packages/trie/__test__/oj/word-break-ii.ts

-45
This file was deleted.

Diff for: packages/trie/__test__/oj/word-search-ii.ts

-58
This file was deleted.

0 commit comments

Comments
 (0)