mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-12 01:00:34 +08:00
Simplify kwfilter
This commit is contained in:
parent
7458d6ff86
commit
b7a11b55ed
12
Build/lib/aho-corasick.test.ts
Normal file
12
Build/lib/aho-corasick.test.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// eslint-disable-next-line import/no-unresolved -- bun
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import createKeywordFilter from './aho-corasick';
|
||||
|
||||
describe('AhoCorasick', () => {
|
||||
it('basic', () => {
|
||||
const kwfilter = createKeywordFilter(['ap', 'an']);
|
||||
expect(kwfilter('bananan')).toBeTrue();
|
||||
expect(kwfilter('apple')).toBeTrue();
|
||||
expect(kwfilter('melon')).toBeFalse();
|
||||
});
|
||||
});
|
||||
@ -1,95 +1,83 @@
|
||||
interface Node {
|
||||
/** @default 0 */
|
||||
depth?: number,
|
||||
key: string,
|
||||
/** @default false */
|
||||
word?: boolean,
|
||||
children: Record<string, Node>,
|
||||
fail?: Node,
|
||||
count: number
|
||||
wordEnd?: boolean,
|
||||
children: Map<string, Node | undefined>,
|
||||
fail?: Node
|
||||
}
|
||||
|
||||
const createNode = (key: string, depth = 0): Node => ({
|
||||
depth,
|
||||
key,
|
||||
word: false,
|
||||
children: {},
|
||||
fail: undefined,
|
||||
count: 0
|
||||
const createNode = (): Node => ({
|
||||
wordEnd: false,
|
||||
children: new Map(),
|
||||
fail: undefined
|
||||
});
|
||||
|
||||
const createKeywordFilter = (keys: string[] | Set<string>) => {
|
||||
const root = createNode('root');
|
||||
const root = createNode();
|
||||
|
||||
const build = () => {
|
||||
const put = (key: string, len = key.length) => {
|
||||
let node = root;
|
||||
const lastIdx = len - 1;
|
||||
|
||||
for (let idx = 0; idx < len; idx++) {
|
||||
const char = key[idx];
|
||||
|
||||
if (node.children.has(char)) {
|
||||
node = node.children.get(char)!;
|
||||
} else {
|
||||
const newNode = createNode();
|
||||
node.children.set(char, newNode);
|
||||
node = newNode;
|
||||
}
|
||||
|
||||
if (lastIdx === idx && node !== root) {
|
||||
node.wordEnd = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
keys.forEach(k => put(k));
|
||||
|
||||
// const build = () => {
|
||||
const queue: Node[] = [];
|
||||
queue.push(root);
|
||||
|
||||
let idx = 0;
|
||||
while (queue.length > idx) {
|
||||
const beginNode = queue[idx];
|
||||
const map = beginNode.children;
|
||||
// eslint-disable-next-line guard-for-in -- plain object
|
||||
for (const key in beginNode.children) {
|
||||
const node = map[key];
|
||||
const children = beginNode.children;
|
||||
|
||||
children.forEach((node, char) => {
|
||||
let failNode = beginNode.fail;
|
||||
|
||||
while (failNode && !failNode.children[key]) {
|
||||
while (failNode && !failNode.children.has(char)) {
|
||||
failNode = failNode.fail;
|
||||
}
|
||||
|
||||
if (node) {
|
||||
node.fail = failNode?.children[key] || root;
|
||||
node.fail = failNode?.children.get(char) || root;
|
||||
|
||||
queue.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
idx++;
|
||||
}
|
||||
};
|
||||
|
||||
const put = (key: string, len: number) => {
|
||||
let node = root;
|
||||
const lastIdx = len - 1;
|
||||
node.count++;
|
||||
for (let idx = 0; idx < len; idx++) {
|
||||
const val = key[idx];
|
||||
const nextNode = node.children[val];
|
||||
|
||||
if (nextNode) {
|
||||
nextNode.count++;
|
||||
node = nextNode;
|
||||
} else {
|
||||
const newNode = createNode(val, idx + 1);
|
||||
newNode.count = 1;
|
||||
node.children[val] = newNode;
|
||||
node = newNode;
|
||||
}
|
||||
|
||||
if (lastIdx === idx && node.depth) {
|
||||
node.word = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
keys.forEach(k => put(k, k.length));
|
||||
|
||||
build();
|
||||
// };
|
||||
// build();
|
||||
|
||||
return (text: string) => {
|
||||
let node: Node | undefined = root;
|
||||
|
||||
for (let i = 0, textLen = text.length; i < textLen; i++) {
|
||||
// const key = text.charAt(i);
|
||||
const key = text[i];
|
||||
const char = text[i];
|
||||
|
||||
while (node && !node.children[key]) {
|
||||
while (node && !node.children.has(char)) {
|
||||
node = node.fail;
|
||||
}
|
||||
node = node?.children[key] || root;
|
||||
node = node?.children.get(char) || root;
|
||||
|
||||
if (node.word) {
|
||||
if (node.wordEnd) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user