从排序的单词列表中有效地打印匹配的semordnilaps对

时间:2015-04-03 18:53:01

标签: python algorithm python-2.7

我正在研究从给定的按字母顺序排列的单词(或短语)列表中打印所有匹配的semordnilaps对的问题(假设为小写)。

semordnilap 被定义为向后拼写不同单词(或短语)的单词(或短语)。所以'top''pot'向后阅读),'avid''diva'向后阅读)和'animal''lamina'向后读)是semordnilaps,就像'semordnilap'本身一样,因为它是'palindromes' 向后读,而'tot''peep''雷达'是回文(向后读取相同的词)但不是semordnilaps。在这种情况下,如果'word1',那么一对单词'word1''word2' 匹配 >'word2'向后读(反之亦然)。

如果输入列表的长度为N,那么解决方案显然会有复杂度O(N(N-1)/2),因为可以构造N(N-1)/2个不同的对。此外,如果列表按字母顺序排序,那么在最坏的情况下,必须检查所有N(N-1)/2对以找到所有匹配对。

我想知道是否有更有效的方法来做到这一点,而不是直截了当的方式。这是我的代码,目前。

import io

def semordnilaps_in_text_file( file_path ):

    def pairup( alist ):
        for elem1 in range( len( alist ) ):
            for elem2 in range( elem1 + 1 , len( alist ) ):
                yield ( alist[elem1], alist[elem2] )

    def word_list( file_path ):
        thelist = []
        with io.open( file_path, 'r', encoding='utf-8' ) as file:
            for line in file:
                thelist.append( line.strip() )
        return thelist

    for word1, word2 in pairup( word_list( file_path ) ):
        if word1[::-1] == word2:
            print '{} {}'.format( word1, word2 )

我尝试了这个函数,其中包含一个(全部小写的)英文单词列表here(包含109583个单词),并在几分钟后设法打印以下21对,然后我打断它。

abut tuba
ac ca
ados soda
agar raga
ah ha
ajar raja
al la
am ma
an na
animal lamina
ante etna
ape epa
are era
ares sera
as sa
assam massa
ate eta
avid diva
aw wa
bad dab
bag gab

3 个答案:

答案 0 :(得分:2)

您可以做的一件事是使用哈希表预处理单词。回文必须具有相同的字母数,所以只需要制作如下的字典映射:

opt => [pot, top, opt]

然后你只需迭代列表并重复你的慢速方法。这句话因为它仍然使用你的O(N ^ 2)算法,但是通过仅比较有可能是半圆形的东西,使N变得更小。您可以仅根据长度使用相同的想法,其中长度为3的所有单词都在一个桶中。这看起来像这样:

3 => [pot, top, opt, cat, act, tac, art, tar, hop, ...]

然而,这比使用键取决于单词构成要慢得多,因为只使用长度,你可以比较top,pot和opt到所有其他三个字母的单词。

以下是一些代码,在我的笔记本电脑上发现了一秒钟内有281个semordnilaps:

#!/usr/bin/python

import collections

def xform(word):
   return ''.join(sorted(list(word)))

wordmap = collections.defaultdict(lambda: [])
for line in open('wordsEn.txt', 'r'):
    word = line.rstrip()
    key = xform(word)
    wordmap[key].append(word)

for key, words in wordmap.iteritems():
    for index1 in xrange(len(words)):
        for index2 in xrange(index1 + 1, len(words)):
            word1 = words[index1]
            word2 = words[index2]
            if word1[::-1] == word2:
                print word1, ' ', word2

结果可从here获得。 值得注意的是,排序单词列表并不能真正帮助你,因为semordnilaps将分散在整个列表中。

答案 1 :(得分:2)

你只需要跟踪你所看到的单词。

def pairup(alist):
  seen = set()
  for word in alist:
    if word not in seen:
      # Haven't seen this one yet
      if word[::-1] in seen:
        # But we've seen its reverse, so we found a pair
        yield (word, word[::-1])
      # Now we've seen it
      seen.add(word)

微妙:在结尾处将新发现的单词添加到seen可以避免在遇到回文时触发收益。相反,如果您还想检测回文,请在检查反射是否已存在之前将该词添加到seen

另外:没有必要将单词读入列表以使用该功能。你可以只提供一个可迭代的,比如列表理解:

for word, drow in pairup(line.strip().lower()
                         for line in io.open(filepath, 'r')):
  print('{} {}'.format(word, drow))

答案 2 :(得分:0)

您可以在此处使用词典访问O(1)中的单词。

words=open('words.txt','r')
word_dict={} #dictionary to store all the words
for word in words:
    word = word.strip('\n')
    if word!=word[::-1]: #remove the palindromic words
        word_dict[word] = ''

for word in word_dict.keys():
    try:
        word_dict[word] = word[::-1]
        #delete the semordnilaps from dictionary
        del word_dict[word[::-1]] 
    except KeyError:
        #if word has no semordnilaps then remove it from dictionary
        del word_dict[word]

#word_dict is the desired dictionary
print word_dict,"\nTotal words: \n",len(word_dict)

我使用了' del'从字典中删除不需要的单词,从而减少时间复杂度和异常处理'访问O(1)中的单词。 希望它有所帮助。