-
Notifications
You must be signed in to change notification settings - Fork 283
Expand file tree
/
Copy pathmod.js
More file actions
235 lines (205 loc) · 6.41 KB
/
mod.js
File metadata and controls
235 lines (205 loc) · 6.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/**
* 字符串距离计算模块
*
* 该模块实现了Myers算法来计算两个字符串之间的编辑距离(Levenshtein距离)。
* 提供了高效的字符串相似度计算和最近匹配查找功能。
*
* Myers算法是一种高效的编辑距离计算算法,时间复杂度为O(ND),其中N是字符串长度,D是编辑距离。
*
* @author drpy-node
* @version 1.0.0
*/
// 预分配的位向量数组,用于Myers算法的字符匹配
const peq = new Uint32Array(0x10000);
/**
* Myers算法的32位实现
* 用于计算较短字符串(长度<=32)的编辑距离
*
* @param {string} a - 第一个字符串
* @param {string} b - 第二个字符串
* @returns {number} 编辑距离
*/
const myers_32 = (a, b) => {
const n = a.length;
const m = b.length;
const lst = 1 << (n - 1); // 最高位掩码
let pv = -1; // 正向量(所有位为1)
let mv = 0; // 负向量(所有位为0)
let sc = n; // 当前得分
let i = n;
// 构建字符匹配位向量
while (i--) {
peq[a.charCodeAt(i)] |= 1 << i;
}
// 逐字符处理字符串b
for (i = 0; i < m; i++) {
let eq = peq[b.charCodeAt(i)]; // 当前字符的匹配位向量
const xv = eq | mv;
eq |= ((eq & pv) + pv) ^ pv; // 计算水平差异
mv |= ~(eq | pv); // 更新负向量
pv &= eq; // 更新正向量
// 更新得分
if (mv & lst) {
sc++;
}
if (pv & lst) {
sc--;
}
// 移位操作
mv = (mv << 1) | 1;
pv = (pv << 1) | ~(xv | mv);
mv &= xv;
}
// 清理位向量
i = n;
while (i--) {
peq[a.charCodeAt(i)] = 0;
}
return sc;
};
/**
* Myers算法的扩展实现
* 用于计算较长字符串的编辑距离,支持任意长度
*
* @param {string} b - 第一个字符串
* @param {string} a - 第二个字符串
* @returns {number} 编辑距离
*/
const myers_x = (b, a) => {
const n = a.length;
const m = b.length;
const mhc = []; // 负水平进位数组
const phc = []; // 正水平进位数组
const hsize = Math.ceil(n / 32); // 水平块数
const vsize = Math.ceil(m / 32); // 垂直块数
// 初始化水平进位数组
for (let i = 0; i < hsize; i++) {
phc[i] = -1;
mhc[i] = 0;
}
let j = 0;
// 处理除最后一个垂直块外的所有块
for (; j < vsize - 1; j++) {
let mv = 0;
let pv = -1;
const start = j * 32;
const vlen = Math.min(32, m) + start;
// 构建当前块的字符匹配位向量
for (let k = start; k < vlen; k++) {
peq[b.charCodeAt(k)] |= 1 << k;
}
// 处理字符串a的每个字符
for (let i = 0; i < n; i++) {
const eq = peq[a.charCodeAt(i)];
const pb = (phc[(i / 32) | 0] >>> i) & 1; // 正水平进位位
const mb = (mhc[(i / 32) | 0] >>> i) & 1; // 负水平进位位
const xv = eq | mv;
const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;
let ph = mv | ~(xh | pv);
let mh = pv & xh;
// 更新水平进位
if ((ph >>> 31) ^ pb) {
phc[(i / 32) | 0] ^= 1 << i;
}
if ((mh >>> 31) ^ mb) {
mhc[(i / 32) | 0] ^= 1 << i;
}
// 移位操作
ph = (ph << 1) | pb;
mh = (mh << 1) | mb;
pv = mh | ~(xv | ph);
mv = ph & xv;
}
// 清理当前块的位向量
for (let k = start; k < vlen; k++) {
peq[b.charCodeAt(k)] = 0;
}
}
// 处理最后一个垂直块
let mv = 0;
let pv = -1;
const start = j * 32;
const vlen = Math.min(32, m - start) + start;
// 构建最后一个块的字符匹配位向量
for (let k = start; k < vlen; k++) {
peq[b.charCodeAt(k)] |= 1 << k;
}
let score = m; // 初始得分
// 处理字符串a的每个字符
for (let i = 0; i < n; i++) {
const eq = peq[a.charCodeAt(i)];
const pb = (phc[(i / 32) | 0] >>> i) & 1;
const mb = (mhc[(i / 32) | 0] >>> i) & 1;
const xv = eq | mv;
const xh = ((((eq | mb) & pv) + pv) ^ pv) | eq | mb;
let ph = mv | ~(xh | pv);
let mh = pv & xh;
// 更新得分
score += (ph >>> (m - 1)) & 1;
score -= (mh >>> (m - 1)) & 1;
// 更新水平进位
if ((ph >>> 31) ^ pb) {
phc[(i / 32) | 0] ^= 1 << i;
}
if ((mh >>> 31) ^ mb) {
mhc[(i / 32) | 0] ^= 1 << i;
}
// 移位操作
ph = (ph << 1) | pb;
mh = (mh << 1) | mb;
pv = mh | ~(xv | ph);
mv = ph & xv;
}
// 清理最后一个块的位向量
for (let k = start; k < vlen; k++) {
peq[b.charCodeAt(k)] = 0;
}
return score;
};
/**
* 计算两个字符串之间的编辑距离
* 自动选择最优算法(32位或扩展版本)
*
* @param {string} a - 第一个字符串
* @param {string} b - 第二个字符串
* @returns {number} 编辑距离(Levenshtein距离)
*/
const distance = (a, b) => {
// 确保a是较长的字符串,优化算法性能
if (a.length < b.length) {
const tmp = b;
b = a;
a = tmp;
}
// 空字符串的距离就是另一个字符串的长度
if (b.length === 0) {
return a.length;
}
// 根据字符串长度选择算法
if (a.length <= 32) {
return myers_32(a, b); // 使用32位优化版本
}
return myers_x(a, b); // 使用扩展版本
};
/**
* 在字符串数组中找到与目标字符串最相似的字符串
*
* @param {string} str - 目标字符串
* @param {string[]} arr - 候选字符串数组
* @returns {string} 最相似的字符串
*/
const closest = (str, arr) => {
let min_distance = Infinity; // 最小距离
let min_index = 0; // 最小距离对应的索引
// 遍历所有候选字符串
for (let i = 0; i < arr.length; i++) {
const dist = distance(str, arr[i]);
if (dist < min_distance) {
min_distance = dist;
min_index = i;
}
}
return arr[min_index];
};
// 导出公共函数
export { closest, distance };