From 768c60cf6c60cd0370f70492cc8f5e0bc4979347 Mon Sep 17 00:00:00 2001 From: Leon Date: Thu, 6 May 2021 06:21:56 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20KMP=20=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __test__/strings/index.spec.ts | 16 ++++++------ src/algs4/strings/kmp.ts | 47 +++++++++++++++++----------------- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/__test__/strings/index.spec.ts b/__test__/strings/index.spec.ts index 88e4a378..b7029bc7 100644 --- a/__test__/strings/index.spec.ts +++ b/__test__/strings/index.spec.ts @@ -172,12 +172,12 @@ describe('字符串算法测试', () => { }) }) - // describe('KMP', () => { - // test('KMP 构造函数 lix', () => { - // const kmp = new KMP('ababac') - // expect(kmp).not.toBeNull() - // const index = kmp.search('aaaaabbbbbababacaaaaaaaaaaaaaaaaaa') - // expect(index).toBe(0) - // }) - // }) + describe('KMP', () => { + test('KMP 构造函数 lix', () => { + const kmp = new KMP('ababac') + expect(kmp).not.toBeNull() + const index = kmp.search('aaaaabbbbbababacaaaaaaaaaaaaaaaaaa') + expect(index).toBe(10) + }) + }) }) diff --git a/src/algs4/strings/kmp.ts b/src/algs4/strings/kmp.ts index 1efce3f5..88559f82 100644 --- a/src/algs4/strings/kmp.ts +++ b/src/algs4/strings/kmp.ts @@ -1,41 +1,40 @@ -const ACode = 97 +const RADIX = 256 export default class KMP { private pat: string private dfa: number[][] + // 由模式字符串构造 DFA constructor(pat: string) { this.pat = pat - const M = pat.length - const R = 26 - this.dfa = Array.from({ length: R }, () => []) - for (const sub of this.dfa) { - for (let i = 0; i < M; i++) { - sub[i] = null - } + this.dfa = new Array(RADIX) + const n = this.pat.length + for (let i = 0; i < RADIX; i++) { + this.dfa[i] = new Array(n).fill(0) } - this.dfa[this.pat.charCodeAt(0) - ACode][0] = 1 - for (let X = 0, j = 1; j < M; j++) { - for (let c = 0; c < R; c++) { - this.dfa[c][j] = this.dfa[c][X] + this.dfa[this.pat.charCodeAt(0)][0] = 1 + + // 计算 dfa[][curr] + for (let prev = 0, curr = 1; curr < n; curr++) { + for (let ch = 0; ch < RADIX; ch++) { + this.dfa[ch][curr] = this.dfa[ch][prev] // 复制匹配失败情况下的值 } - this.dfa[this.pat.charCodeAt(j) - ACode][j] = j + 1 - X = this.dfa[this.pat.charCodeAt(j) - ACode][X] + this.dfa[this.pat.charCodeAt(curr)][curr] = curr + 1 // 设置匹配成功情况下的值 + prev = this.dfa[this.pat.charCodeAt(curr)][prev] // 更新重启状态 } } - search(txt: string): number { + // 在 txt 上模拟 DFA 的运行 + search(txt: string) { let i, j - const N = txt.length - const M = this.pat.length - for (i = 0, j = 0; i < N && j < M; i++) { - j = this.dfa[txt.charCodeAt(i) - ACode][j] - } - if (j === M) { - return i - M - } else { - return N + const m = this.pat.length + const n = txt.length + for (i = 0, j = 0; i < n && j < m; i++) { + j = this.dfa[txt.charCodeAt(i)][j] } + + // 找到匹配(到达模式字符串的结尾)则返回 i-m + return j === m ? i - m : -1 } }