简体   繁体   中英

Finding All Possible Combinations of Letters using an API and Python

I'm trying to put together a small web app where users enter in the last 4 digits of their phone number, hit submit, and then are shown all the words (if any) that can be made on the keypad using their 4 numbers. So if a user enters " 2287 " they are shown " acts " " bats " " cats " and any other words that can be made from the combination of " a / b / c " and " a / b / c " and " t / u / v " and " p / q / r / s ".

I'm new to web apps and web scripting but I've figured out how to do most of the steps involved. The problems I'm having are:

  1. Creating all possible combinations of the letters

  2. Figuring out which combinations are actually words

I would think you could do some of this using an API but I don't know where to look for something like that. If I used a some sort dictionary API could I enter in all the possible combinations at once, or would I have to make (roughly) 81 different API calls to check if each combination is a real word.

I'm obviously a pretty big noob when it comes to stuff like this, but I'm trying to start off with something simple like this to try and get myself acquainted with server-side web scripting. Also, if at all possible, It'd be awesome if I everything was done in Python as that is the language we are using in my internet applications class and it would make sense to just stick with one language at first. Anyways, thanks in advance.

Read dictionary and keep only 4 letter words, append them to defaultdict(list) by the number code of the four letters, when user inputs for numbers give them the ready list for that number or say 'No words for that number'.

import itertools
letters = ('',' ', 'abc','def','ghi','jkl','mno','pqrs','tuv','wxyz')
reverse_dict = dict((str(ind), group)
                    for ind,group in enumerate(letters)
                    for c in group
                    if letters)

print reverse_dict
end_nums = '3965'
print end_nums, 'can produce', tuple(''.join(w) for w in itertools.product(*map(reverse_dict.get, end_nums)))
word = 'word'
print('%r = %s' % (word,''.join(key for c in word for key, item in reverse_dict.items() if c in item)))

A naive approach would be

import itertools

letters = ('','','abc','def','ghi','jkl','mno','pqrs','tuv','wxyz')
good_words = set(['wall','base','cats'])  # etc

def allWords(s):
    s = [int(ch) for ch in s.zfill(4)]
    for word in itertools.product(letters[s[0]], letters[s[1]], letters[s[2]], letters[s[3]]):
        word = ''.join(word)
        if word in good_words:
            yield word

words = list(allWords('2297'))

A more efficient one would preprocess all good words into a dictionary of per-telephone-number lists and just do a lookup:

import string
import collections

class CodingError(Exception):
    pass

class TelephoneWords(object):
    numbers = {
        'a': '2', 'b': '2', 'c': '2', 'd': '3',
        'e': '3', 'f': '3', 'g': '4', 'h': '4',
        'i': '4', 'j': '5', 'k': '5', 'l': '5',
        'm': '6', 'n': '6', 'o': '6', 'p': '7',
        'q': '7', 'r': '7', 's': '7', 't': '8',
        'u': '8', 'v': '8', 'w': '9', 'x': '9',
        'y': '9', 'z': '9', '0': '0', '1': '1',
        '2': '2', '3': '3', '4': '4', '5': '5',
        '6': '6', '7': '7', '8': '8', '9': '9'
    }
    wordlen = 4

    def __init__(self, wordlist=None, fname=None):
        super(TelephoneWords,self).__init__()
        self.words = collections.defaultdict(list)
        if wordlist:
            self.addwords(wordlist)
        if fname:
            with open(fname) as inf:
                filestr = ' '.join(inf.readlines()).replace(string.punctuation, ' ')
                self.addwords(filestr.split())

    def addwords(self, words):
        _wordlen = TelephoneWords.wordlen
        _words   = self.words
        _encode  = self.encode
        for word in words:
            if len(word)==_wordlen:
                word = word.lower()
                try:
                    _words[_encode(word)].append(word)
                except CodingError:
                    pass

    def addword(self, word):
        self.addwords((word,))

    def encode(self, s):
        _numbers = TelephoneWords.numbers
        res = []
        for ch in s:
            try:
                res.append(_numbers[ch])
            except KeyError:
                # no corresponding value found
                raise CodingError("No value available for char '{0}'".format(ch))
        return ''.join(res)

    def seek(self, num):
        s = str(num).strip().zfill(TelephoneWords.wordlen)
        try:
            return self.words[s]
        except KeyError:
            raise ValueError("No words found for '{0}'".format(s))

    def find(self, num, onErr=None):
        try:
            return self.seek(num)
        except ValueError:
            return [] if onErr is None else onErr

def main():
    tw = TelephoneWords(fname='four_letter_words.txt')

    for word in tw.find('2287'):
        print word

if __name__=="__main__":
    main()

Using a Scrabble wordlist, this gives me

acts
bats
baur
cats
caup

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM