简单的英文变位词聚类算法
有时,简单的算法也有其实用的意义,由于之前公司内部搜索引擎优化的需要,我根据《编程珠玑》中查找英文单词变位词的算法,来实现搜索纠错的功能。
在搜索时,有时记不住单词,会出现拼写错误的情况,例如,搜索“height”时手误,搜索了“heigth”,那么我们要在搜索不到的情况下,给他变位词作为提示(注:因为公司内部搜索,该算法已经够用,真正的搜索引擎应该是采用更高效的算法,请有经验的前辈赐教)。
算法分三步:①对单词签名; ②根据签名的字典序排序; ③根据排序结果挤压,使变位词聚合在一起;
a) 快速单词签名算法(当前仅使用于英文单词,如果包含其他字符,需做拓展):
鉴于英文字母可枚举且数量只有26个,故这里采用变形的基数排序算法,以实现快速签名:
Step 1: 定义一个26位的 int[] 数组,数组从0~25分别表示:A,B,C,D …… X,Y,Z (如图1),初始化全部为0:
A |
B |
C |
D |
E |
F |
G |
H |
I |
J |
K |
L |
M |
N |
O |
P |
G |
R |
S |
T |
U |
V |
W |
X |
Y |
Z |
|
(图1)
Step 2: 将英文字母转换统一(大小写统一),如 Jary → jary,再使用如下公式:存入下标为x-97的位置,例如 a的ascii码为97,则它对应存入byte[]数组中的97-97=0的位置,即该位置自增1;以此类推。
Stpe 3: 给单词签名,从0到25扫描数组,例如 what → ahtw, wath → ahtw, system → ems2ty ;
这样我们就可以只通过一次扫描来获取一个单词的签名,代码如下:
1 public static string SignWord(string word) 2 { 3 int[] dict = new int[26]; 4 5 var array = word.ToLower().ToArray(); 6 foreach(var a in array) { 7 dict[a - 97]++; 8 } 9 StringBuilder sb = new StringBuilder(); 10 for (var i = 0; i < 26; i++) { 11 if (dict[i] != 0) { 12 var t = dict[i].ToString().Equals("1") ? "": dict[i].ToString(); 13 sb.Append((char)(i + 97) + t); 14 } 15 } 16 return sb.ToString(); 17 }
b) 相同签名的单词的挤压,例如,what跟wath有相同的签名,则放到一起,我们可以根据签名的字典排序,但是,使用C#提供的Dictnary可以快速排序加压,代码如下:
1 public static Dictionary < string,List < string >> SqueezeList(string filePath) 2 { 3 Dictionary < string,List < string >> wordDict = new Dictionary < string,List < string >> (); 4 StreamReader fs = new StreamReader(filePath, Encoding.Default); 5 string line; 6 while (!string.IsNullOrEmpty(line = fs.ReadLine())) { 7 var key = SignWord(GetCleanString(line)); 8 if (wordDict.ContainsKey(key)) { 9 wordDict[key].Add(line); 10 } else { 11 var list = new List < string > () { 12 line 13 }; 14 wordDict.Add(key, list); 15 } 16 } 17 fs.Close(); 18 return wordDict; 19 }
这样,我们就将单词以签名为Key,放入到 Dictionary 中;当然,实际应用中,我们会以将Dictionary序列化到文本文件中,这样就不用每次使用时都算一次了;
PS:对于搜索方面的学习,都是看着论文什么的摸索前进,其中走了很多弯路和歪解,如果本文有什么纰漏或错误的地方,请指出和谅解,谢谢;
多聚旅游 聚游宝 学友网