Skip to content

Commit 39a26a1

Browse files
committed
✨ feat: implement '@algorithm.ts/trie'
1 parent 1acaaa3 commit 39a26a1

File tree

10 files changed

+526
-0
lines changed

10 files changed

+526
-0
lines changed

Diff for: README.md

+2
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ Package | Description
6464
[@algorithm.ts/mcmf][] | MCMF algorithm. #MinCostMaxFlow, #NetworkFlow.
6565
[@algorithm.ts/priority-queue][] | Priority Queue (heap).
6666
[@algorithm.ts/sudoku][] | A collection of utilities to generate / solve Sudoku problems.
67+
[@algorithm.ts/trie][] | Trie. (digital tree or prefix tree)
6768

6869

6970
## License
@@ -82,3 +83,4 @@ algorithm.ts is [MIT licensed](https://github.com/guanghechen/algorithm.ts/blob/
8283
[@algorithm.ts/mcmf]: ./packages/mcmf
8384
[@algorithm.ts/priority-queue]: ./packages/priority-queue
8485
[@algorithm.ts/sudoku]: ./packages/sudoku
86+
[@algorithm.ts/trie]: ./packages/trie

Diff for: packages/trie/README.md

+154
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<header>
2+
<h1 align="center">
3+
<a href="https://github.com/guanghechen/algorithm.ts/tree/main/packages/trie#readme">@algorithm.ts/trie</a>
4+
</h1>
5+
<div align="center">
6+
<a href="https://www.npmjs.com/package/@algorithm.ts/trie">
7+
<img
8+
alt="Npm Version"
9+
src="https://img.shields.io/npm/v/@algorithm.ts/trie.svg"
10+
/>
11+
</a>
12+
<a href="https://www.npmjs.com/package/@algorithm.ts/trie">
13+
<img
14+
alt="Npm Download"
15+
src="https://img.shields.io/npm/dm/@algorithm.ts/trie.svg"
16+
/>
17+
</a>
18+
<a href="https://www.npmjs.com/package/@algorithm.ts/trie">
19+
<img
20+
alt="Npm License"
21+
src="https://img.shields.io/npm/l/@algorithm.ts/trie.svg"
22+
/>
23+
</a>
24+
<a href="#install">
25+
<img
26+
alt="Module Formats: cjs, esm"
27+
src="https://img.shields.io/badge/module_formats-cjs%2C%20esm-green.svg"
28+
/>
29+
</a>
30+
<a href="https://github.com/nodejs/node">
31+
<img
32+
alt="Node.js Version"
33+
src="https://img.shields.io/node/v/@algorithm.ts/trie"
34+
/>
35+
</a>
36+
<a href="https://github.com/facebook/jest">
37+
<img
38+
alt="Tested with Jest"
39+
src="https://img.shields.io/badge/tested_with-jest-9c465e.svg"
40+
/>
41+
</a>
42+
<a href="https://github.com/prettier/prettier">
43+
<img
44+
alt="Code Style: prettier"
45+
src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"
46+
/>
47+
</a>
48+
</div>
49+
</header>
50+
<br/>
51+
52+
53+
A typescript implementation of the **TRIE** data structure.
54+
55+
The following definition is quoted from Wikipedia (https://en.wikipedia.org/wiki/Trie):
56+
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.
64+
65+
66+
## Install
67+
68+
* npm
69+
70+
```bash
71+
npm install --save @algorithm.ts/trie
72+
```
73+
74+
* yarn
75+
76+
```bash
77+
yarn add @algorithm.ts/trie
78+
```
79+
80+
## Usage
81+
82+
* Trie
83+
- `init(): void`: Initialize a trie.
84+
85+
- `insert(str: string, v: T, start?: number, end?: number): void`: Insert a
86+
string into the trie.
87+
88+
- `match(str: string, start?: number, end?: number): T | null`: Find a word in
89+
the trie which exact match the `str.slice(start, end)`. If there is such a
90+
word, return its additional value, otherwise return null.
91+
92+
- `find(str: string, start?: number, end?: number): TrieNodeData<T> | null`:
93+
Find word with smallest length in the trie which exact match the
94+
`str.slice(start, x)`, where the x is an integer in the range [start, _end).
95+
96+
- `findAll(str: string, start?: number, end?: number): Array<TrieNodeData<T>>`:
97+
Find all words in the trie which exact match the
98+
`str.slice(start, x)`, where the x is an integer in the range [start, _end).
99+
100+
* Solve https://leetcode.com/problems/word-break-ii/:
101+
102+
```typescript
103+
import { Trie, createTrie } from '@algorithm.ts/trie'
104+
105+
const trie: Trie = createTrie({
106+
SIGMA_SIZE: 26,
107+
ZERO: 0,
108+
idx: (c: string): number => c.codePointAt(0)! - 97,
109+
mergeAdditionalValues: (x, y) => y,
110+
})
111+
112+
export function wordBreak(s: string, wordDict: string[]): string[] {
113+
if (s.length <= 0) return []
114+
115+
trie.init()
116+
for (let i = 0; i < wordDict.length; ++i) {
117+
const word = wordDict[i]
118+
trie.insert(word, i + 1)
119+
}
120+
121+
const N = s.length
122+
const results: string[] = []
123+
const collect: number[] = []
124+
dfs(0, 0)
125+
return results
126+
127+
function dfs(cur: number, pos: number): void {
128+
if (pos === N) {
129+
results.push(
130+
collect
131+
.slice(0, cur)
132+
.map(x => wordDict[x - 1])
133+
.join(' '),
134+
)
135+
return
136+
}
137+
138+
const pairs = trie.findAll(s, pos)
139+
for (const { end, val } of pairs) {
140+
collect[cur] = val
141+
dfs(cur + 1, end)
142+
}
143+
}
144+
}
145+
```
146+
147+
148+
149+
## Related
150+
151+
* https://en.wikipedia.org/wiki/Trie
152+
153+
154+
[homepage]: https://github.com/guanghechen/algorithm.ts/tree/main/packages/trie#readme

Diff for: packages/trie/__test__/trie.spec.ts

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import type { Trie } from '../src'
2+
import { createTrie } from '../src'
3+
4+
describe('trie', function () {
5+
describe('basic', function () {
6+
const trie: Trie = createTrie({
7+
SIGMA_SIZE: 62,
8+
ZERO: 0,
9+
idx: (c: string): number => {
10+
if (/\d/.test(c)) return c.codePointAt(0)! - 48 // digits
11+
if (/[A-Z]/.test(c)) return c.codePointAt(0)! - 65 // uppercase letters
12+
if (/[a-z]/.test(c)) return c.codePointAt(0)! - 97 // lowercase letters
13+
throw new TypeError(`Bad character: (${c})`)
14+
},
15+
mergeAdditionalValues: (x, y) => x + y,
16+
})
17+
18+
test('match', function () {
19+
trie.init()
20+
trie.insert('cat', 1)
21+
trie.insert('cat2', 2)
22+
23+
expect(trie.match('cat')).toBe(1)
24+
// expect(trie.match('cat2')).toBe(2)
25+
expect(trie.match('cat2', 0, 3)).toBe(1)
26+
expect(trie.match('cat3')).toBe(null)
27+
})
28+
29+
test('findAll', function () {
30+
trie.init()
31+
trie.insert('ban', 2)
32+
trie.insert('banana', 1)
33+
trie.insert('apple', 3)
34+
35+
expect(trie.find('ban')).toEqual({ end: 3, val: 2 })
36+
expect(trie.find('bana')).toEqual({ end: 3, val: 2 })
37+
expect(trie.find('banana')).toEqual({ end: 3, val: 2 })
38+
expect(trie.find('apple')).toEqual({ end: 5, val: 3 })
39+
expect(trie.find('2apple')).toEqual(null)
40+
expect(trie.find('2apple', 1)).toEqual({ end: 6, val: 3 })
41+
42+
expect(trie.findAll('bana')).toEqual([{ end: 3, val: 2 }])
43+
expect(trie.findAll('banana')).toEqual([
44+
{ end: 3, val: 2 },
45+
{ end: 6, val: 1 },
46+
])
47+
expect(trie.findAll('banana', 0, 3)).toEqual([{ end: 3, val: 2 }])
48+
})
49+
})
50+
})
51+
52+
describe('leetcode', function () {
53+
test('word-break-ii', function () {
54+
const trie: Trie = createTrie({
55+
SIGMA_SIZE: 26,
56+
ZERO: 0,
57+
idx: (c: string): number => c.codePointAt(0)! - 97,
58+
mergeAdditionalValues: (x, y) => y,
59+
})
60+
61+
const data: Array<{ input: [string, string[]]; answer: string[] }> = [
62+
{
63+
input: ['catsanddog', ['cat', 'cats', 'and', 'sand', 'dog']],
64+
answer: ['cat sand dog', 'cats and dog'],
65+
},
66+
{
67+
input: [
68+
'pineapplepenapple',
69+
['apple', 'pen', 'applepen', 'pine', 'pineapple'],
70+
],
71+
answer: [
72+
'pine apple pen apple',
73+
'pine applepen apple',
74+
'pineapple pen apple',
75+
],
76+
},
77+
{
78+
input: ['catsandog', ['cats', 'dog', 'sand', 'and', 'cat']],
79+
answer: [],
80+
},
81+
]
82+
83+
for (const { input, answer } of data) {
84+
expect(wordBreak(input[0], input[1])).toEqual(answer)
85+
}
86+
87+
/**
88+
* A solution for leetcode https://leetcode.com/problems/word-break-ii/
89+
*
90+
* @author guanghechen
91+
*/
92+
function wordBreak(s: string, wordDict: string[]): string[] {
93+
if (s.length <= 0) return []
94+
95+
trie.init()
96+
for (let i = 0; i < wordDict.length; ++i) {
97+
const word = wordDict[i]
98+
trie.insert(word, i + 1)
99+
}
100+
101+
const N = s.length
102+
const results: string[] = []
103+
const collect: number[] = []
104+
dfs(0, 0)
105+
return results
106+
107+
function dfs(cur: number, pos: number): void {
108+
if (pos === N) {
109+
results.push(
110+
collect
111+
.slice(0, cur)
112+
.map(x => wordDict[x - 1])
113+
.join(' '),
114+
)
115+
return
116+
}
117+
118+
const pairs = trie.findAll(s, pos)
119+
for (const { end, val } of pairs) {
120+
collect[cur] = val
121+
dfs(cur + 1, end)
122+
}
123+
}
124+
}
125+
})
126+
})

Diff for: packages/trie/package.json

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "@algorithm.ts/trie",
3+
"version": "1.0.10",
4+
"description": "Dancing link + Algorithm X",
5+
"author": {
6+
"name": "guanghechen",
7+
"url": "https://github.com/guanghechen/"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "https://github.com/guanghechen/algorithm.ts.git",
12+
"directory": "packages/trie"
13+
},
14+
"homepage": "https://github.com/guanghechen/algorithm.ts/tree/main/packages/trie#readme",
15+
"keywords": [
16+
"data structure",
17+
"string",
18+
"trie",
19+
"digital tree",
20+
"prefix tree"
21+
],
22+
"main": "lib/cjs/index.js",
23+
"module": "lib/esm/index.js",
24+
"types": "lib/types/index.d.ts",
25+
"source": "src/index.ts",
26+
"license": "MIT",
27+
"engines": {
28+
"node": ">= 14.15.0"
29+
},
30+
"files": [
31+
"lib/",
32+
"!lib/**/*.js.map",
33+
"!lib/**/*.d.ts.map",
34+
"package.json",
35+
"CHANGELOG.md",
36+
"LICENSE",
37+
"README.md"
38+
],
39+
"scripts": {
40+
"build": "cross-env NODE_ENV=production rollup -c ../../rollup.config.js",
41+
"prebuild": "rimraf lib/ && tsc -p tsconfig.src.json --emitDeclarationOnly",
42+
"prepublishOnly": "cross-env ROLLUP_SHOULD_SOURCEMAP=false yarn build",
43+
"test": "cross-env TS_NODE_FILES=true jest --config ../../jest.config.js --rootDir ."
44+
}
45+
}

Diff for: packages/trie/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './trie'
2+
export * from './types'

0 commit comments

Comments
 (0)