JavaScript 中五种常用的单模式字符串匹配算法
2023-11-19 02:28:26
字符串匹配是计算机科学中的一项基本任务,它广泛应用于各种领域,如文本搜索、自然语言处理、模式识别等。单模式字符串匹配是指在一个字符串(主串)中查找另一个字符串(模式串)。
在 JavaScript 中,有多种算法可以用于单模式字符串匹配,每种算法都有其独特的特点和效率。在本篇文章中,我们将介绍五种最常用的单模式字符串匹配算法:朴素算法、KMP 算法、BM 算法、RK 算法和 BMH 算法。
朴素算法
朴素算法是最简单的一种单模式字符串匹配算法。它的基本思想是逐个字符地比较主串和模式串,如果发现两者的某个位置上的字符不同,则模式串向右移动一位,继续比较。如果模式串中的所有字符都与主串中的字符一一对应,则匹配成功。朴素算法的时间复杂度为 O(n * m),其中 n 是主串的长度,m 是模式串的长度。
function naiveStringMatch(str, pattern) {
for (let i = 0; i < str.length - pattern.length + 1; i++) {
let matched = true;
for (let j = 0; j < pattern.length; j++) {
if (str[i + j] !== pattern[j]) {
matched = false;
break;
}
}
if (matched) {
return i;
}
}
return -1;
}
KMP 算法
KMP 算法是 Knuth-Morris-Pratt 算法的简称,它是一种改进朴素算法的算法。KMP 算法在预处理阶段计算出一个 next 数组,next[i] 表示模式串中第 i 个字符的下一个匹配位置。这样在匹配过程中,当模式串中的某个字符不匹配时,可以根据 next 数组快速跳到下一个匹配位置,从而提高匹配效率。KMP 算法的时间复杂度为 O(n + m),其中 n 是主串的长度,m 是模式串的长度。
function kmpStringMatch(str, pattern) {
const next = computeNext(pattern);
let i = 0;
let j = 0;
while (i < str.length) {
if (str[i] === pattern[j]) {
i++;
j++;
} else {
if (j > 0) {
j = next[j - 1];
} else {
i++;
}
}
if (j === pattern.length) {
return i - j;
}
}
return -1;
function computeNext(pattern) {
const next = [0];
let i = 1;
let j = 0;
while (i < pattern.length) {
if (pattern[i] === pattern[j]) {
next[i] = j + 1;
i++;
j++;
} else {
if (j > 0) {
j = next[j - 1];
} else {
next[i] = 0;
i++;
}
}
}
return next;
}
}
BM 算法
BM 算法是 Boyer-Moore 算法的简称,它是一种改进朴素算法的算法。BM 算法在预处理阶段计算出一个 last 数组,last[c] 表示模式串中最后一个出现字符 c 的位置。这样在匹配过程中,当模式串中的某个字符不匹配时,可以根据 last 数组快速跳到下一个可能匹配的位置,从而提高匹配效率。BM 算法的时间复杂度为 O(n * m),其中 n 是主串的长度,m 是模式串的长度。
function bmStringMatch(str, pattern) {
const last = computeLast(pattern);
let i = pattern.length - 1;
let j = pattern.length - 1;
while (i < str.length) {
if (str[i] === pattern[j]) {
if (j === 0) {
return i;
}
i--;
j--;
} else {
if (last[str[i]] !== -1) {
i = i - j + last[str[i]];
j = pattern.length - 1;
} else {
i += pattern.length;
j = pattern.length - 1;
}
}
}
return -1;
function computeLast(pattern) {
const last = {};
for (let i = 0; i < pattern.length; i++) {
last[pattern[i]] = i;
}
return last;
}
}
RK 算法
RK 算法是 Rabin-Karp 算法的简称,它是一种使用哈希函数进行字符串匹配的算法。RK 算法在预处理阶段计算出模式串的哈希值,然后在匹配过程中逐个字符地计算主串的哈希值,并与模式串的哈希值进行比较。如果两个哈希值相等,则进一步比较主串和模式串的字符,以确认匹配是否成功。RK 算法的时间复杂度为 O(n + m),其中 n 是主串的长度,m 是模式串的长度。
function rkStringMatch(str, pattern) {
const patternHash = computeHash(pattern);
let windowHash = computeHash(str.substring(0, pattern.length));
let i = pattern.length;
while (i < str.length) {
if (windowHash === patternHash) {
if (str.substring(i - pattern.length, i) === pattern) {
return i - pattern.length;
}
}
windowHash = (windowHash - str[i - pattern.length] * Math.pow(26, pattern.length - 1)) * 26 + str[i];
i++;
}
return -1;
function computeHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = hash * 26 + str[i].charCodeAt() - 'a'.charCodeAt();
}
return hash;
}
}
BMH 算法
BMH 算法是 Boyer-Moore-Horspool 算法的简称,它是一种改进 BM 算法的算法。BMH 算法在预处理阶段计算出一个 skip 数组,skip[c] 表示模式串中字符 c 上一次出现的位置。这样在匹配过程中,当模式串中的某个字符不匹配时,可以根据 skip 数组快速跳到下一个可能匹配的位置,从而提高匹配效率。BMH 算法的时间复杂度为 O(n * m),其中 n 是主串的长度,m 是模式串的长度。
function bmhStringMatch(str, pattern) {
const skip = computeSkip(pattern);
let i = pattern.length - 1;
let j = pattern.length - 1;
while (i < str.length) {
if (str[i] === pattern[j]) {
if (j === 0) {
return i;
}
i--;
j--;
} else {
if (skip[str[i]] !== -1) {
i = i - j + skip[str[i]];
j = pattern.length - 1;
} else {
i += pattern.length;
j = pattern.length - 1;
}
}
}
return -1;
function computeSkip(pattern) {
const skip = {};
for (let i = 0; i < pattern.length - 1; i++) {
skip[pattern[i]] = pattern.length - i - 1;
}
return skip;
}
}