UNPKG

ahocorasick-ts

Version:

ahocorasick implemention

88 lines (74 loc) 1.83 kB
class TrieNode { matched: string[] child: Record<string, TrieNode> failure: TrieNode constructor(root?: TrieNode) { this.matched = [] this.child = {} if (root) { this.failure = root } else { this.failure = this } } } export interface SearchResult { end: number keys: string[] } export class AhoCorasick { trie: TrieNode constructor(keyword: string[]) { this.trie = new TrieNode() keyword.forEach(s => { let cur = this.trie for (const ch of s) { if (!(ch in cur.child)) { cur.child[ch] = new TrieNode(this.trie) } cur = cur.child[ch] } cur.matched.push(s) }) const queue: TrieNode[] = [] for (const ch in this.trie.child) { queue.push(this.trie.child[ch]) } while (queue.length > 0) { const cur = queue.shift() as TrieNode for (const ch in cur.child) { queue.push(cur.child[ch]) let f = cur.failure while (f !== this.trie && !(ch in f.child)) { f = f.failure } if (ch in f.child) { cur.child[ch].failure = f.child[ch] cur.child[ch].matched.push(...cur.failure.matched) } else { cur.child[ch].failure = this.trie } } } } search(key: string): SearchResult[] { let cur = this.trie const res: SearchResult[] = [] key.split('').forEach((ch, i) => { while (cur !== this.trie && !(ch in cur.child)) { cur = cur.failure } if (!(ch in cur.child)) { return } cur = cur.child[ch] if (cur.matched.length) { res.push({ end: i, keys: cur.matched, }) } }) return res } }