一次寻找邻居单词列表的算法优化
朋友发来了一道题目进行讨论,题目的主体可以简化为如下:
定义一个单词的邻居为,与其长度相同,有且仅有一个字母不同的其他单词。对于一个单词列表,计算所有单词的邻居列表。
例如:单词de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>与de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>为邻居,而与de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >songde>不为邻居,因为它们长度不一样。
读者朋友们,看完这道题目后,请先进行独立思考,然后再展开阅读。p.s. 本文将不包含具体代码。
暴力法
第一个想法很直白,遍历所有单词,判断彼此是否为邻居,若为邻居,则将彼此加入到自己的邻居列表。
这个算法的复杂度为。n为单词的个数。当单词超过一万时,意味着有一亿次的邻居查找操作。
即使邻居判定的方法再高效,这个方法也是十分低效的。
第一次优化
根据邻居的定义,可知长度不同的单词一定不是邻居,所以对长度不同的单词的互相判断是否为邻居其实是不必要的。
可以先对单词列表进行一次预处理,将长度相同的单词放入到同一个列表中,然后对每一个这样的列表内进行彼此邻居的判定。
假设所有单词的长度可以均分为10份,则每一个列表的长度可约等de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >n/10de>,整个的复杂度为,虽然还是,但是已经比之前的暴力法要高效好几倍。
第二次优化
由于第一次优化并没有将实质性的复杂度降低,所以在单词很多的情况下还是比较低效。
我们需要重新审题,看能不能发现一点什么。
了解正则表达式的同学都知道,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >s?nde>这个匹配可以匹配de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>,也可以匹配de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>。也就是说de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >s?nde>是de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>和de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>的共同匹配。而de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >?unde>则是de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>和de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >gunde>的共同匹配。
邻居单词之间都有某种联系,这种联系就是它们都有一个共同的匹配。所以,我们可以遍历所有单词,建立以匹配为键,匹配的单词列表为值的字典,然后遍历每个匹配的单词列表,这些单词列表中的所有单词都互相为对方的邻居单词。
例如:
- 当遍历到单词de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>时,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>有三个匹配,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >[?un, s?n, su?]de>。匹配字典中将加入这几个匹配,这时候字典的内容为de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >{ ?un=>[sun], s?n=>[sun], su?=>[sun] }de>。
- 当遍历到单词de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>时,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>有三个匹配,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >[?on, s?n, so?]de>。匹配字典中将加入这几个匹配,这时候字典的内容为de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >{ ?un=>[sun], s?n=>[sun, son], su?=>[sun], ?on=>[son], so?=>[son] }de>。其中,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>和de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>共同的匹配de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >s?nde>对应的列表加入了de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sonde>。
- 当遍历到单词de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >gunde>时,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >gunde>有三个匹配,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >[?un, g?n, gu?]de>。匹配字典中将加入这几个匹配,这时候字典的内容为de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >{ ?un=>[sun, gun], s?n=>[sun, son], su?=>[sun], ?on=>[son], so?=>[son], g?n=>[gun], gu?=>[gun] }de>。其中,de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >sunde>和de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >gunde>共同的匹配de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >?unde>对应的列表加入了de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >gunde>。
对于每一个单词,假设单词的长度为de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >Lde>,则单词有de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >Lde>个匹配。对于de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >nde>个单词,则最多有de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >n*Lde>个匹配列表。假设最长的匹配列表的长度为de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >mde>,则整个算法的复杂度为。由于每个单词的邻居不会太多,所以基本可以将de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >mde>视为常数。所以整个算法的复杂度为。
如果当单词列表长度de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >nde>远大于最长单词的长度de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >Lde>时,这个算法的复杂度将为,线性时间。
回顾
不知道有多少同学看过《编程珠玑》。这本书的第二章有一个de style="border: 1px solid rgb(234, 234, 234); font-family: Consolas, 'Courier New', monospace; font-size: 15px; font-style: inherit; font-weight: inherit; margin: 0px 2px; outline: 0px; padding: 0px 5px; vertical-align: baseline; white-space: nowrap; border-radius: 3px; background-color: rgb(248, 248, 248);" >aha momentde>,表示忽然习得的灵感。
其中本文的最后一个算法与第二章的变位词算法有相似之处。都是基于标识(索引)来解决问题。
而解决问题的关键在于发现问题可以用标识来解决,即邻居间的共性是存在一个共同的匹配,而每个单词的匹配都是有限的(跟单词的长度一致)。
转自-http://wuzhiwei.net/find_word_neighbor_optimizition/