In [ ]:
def test(test_name, method, exp_result, *argv):
    if method(*argv) == exp_result:
        print('{} - Success'.format(test_name))
    else:
        print('{} - Failed'.format(test_name))

In [ ]:
'''
Boggle
Memory: O(N^n)
Runtime: O(N^2)
'''

class Boggle:
  #code assumes that both dimensions of grid are same
  def __init__(self, grid, dictionary):
    self.grid = grid
    self.dictionary = dictionary
    self.state = [[False for x in xrange(len(grid))] \
                  for y in xrange(len(grid))]

  def find_all_nbrs(self, x, y): 
    nbrs = []
    for i in xrange(max(0, x - 1), min(x + 2, len(self.grid))):
      for j in xrange(max(0, y - 1), min(y + 2, len(self.grid))):
        if i == x and j == y:
          continue          
        if self.state[i][j] == False:
          nbrs.append([i, j])
    return nbrs

  def find_words_rec(self, i, j, current, words):    
    if len(current) > 0 and (current in self.dictionary):
      words.add(current)

    # we can really speed up our algorithm if we have prefix method available
    # for our dictionary by using code like below
    
    #if not dictionary.is_prefix(current):
    #  // if current word is not prefix of any word in dictionary
    #  // we don't need to continue with search
    #  return

    nbrs = self.find_all_nbrs(i, j)
    for pr in nbrs:  
      first = pr[0]
      second = pr[1]  
      current += self.grid[first][second]
      self.state[first][second] = True
      self.find_words_rec(first, second, current, words)
      current = current[:-1] #equivalent to current = current[0:len(current) - 1]
      self.state[first][second] = False

  def find_all_words(self):
    words = set([])
    for i in xrange(0, len(self.grid)):
      for j in xrange(0, len(self.grid)):
        current_word = ""
        self.find_words_rec(i, j, current_word, words)
    return words
  
def main():
  grid = [
    ['c', 'a', 't'],
    ['r', 'r', 'e'],
    ['t', 'o', 'n']
  ]

  dictionary = set(["cat", "cater", "cartoon", 
    "toon", "moon", "not", "tone", "apple", "ton", "art"])

  b = Boggle(grid, dictionary)
  words = b.find_all_words()
  
  for w in words:
    print w,

main()

Phone Digits

Given a phone number create a list of all the possible words that you can make given a dictionary from numbers to letters.

In python there is a itertools.permutations('abc') that would print all permutations given some input.

import itertools
itertools.permutations('abc')
[i for i in itertools.permutations('abc')]
# output permutations

In [3]:
letters_map = {'2':'ABC', '3':'DEF', '4':'GHI', '5':'JKL', 
               '6':'MNO', '7':'PQRS', '8':'TUV', '9':'WXYZ'}

def printWords(number, ):
    #number is phone number 

def printWordsUtil(numb, curr_digit, output, n):
    
    if curr_digit == n:
        print('%s ' % output)
        return 
    
    for i in range(len(letters_map[numb[curr_digit]])):
        output[curr_digit] = letters_map[number[curr_digit]][i]
        printWordsUtil(numb, curr_digit+1, output, n)
        if numb[curr_digit] == 0 or numb[curr_digit] == 1:
            return

In [ ]:
def gen_phone(digits):
    results = []
    lookup = {
        '0': ' ',
        '1': ' ',
        '2': 'abc',
        '3': 'def',
        '4': 'ghi',
        '5': 'jkl',
        '6': 'mno',
        '7': 'pqrs',
        '8': 'tuv',
        '9': 'wxyz',
    }
    
    def decode_next(s, i):
        if i == len(digits):
            results.append(s)
            return
        
        for c in lookup[digits[i]]:
            decode_next(s + c, i + 1)
            
    decode_next('', 0)
    return results

Print Longest Common Subsequence

This is a good problem for working out variations where you count contiguous subsequence versus non continuous

The move with longest common subsequence is to start from the back of the strings and see if the letters are the same. Then increment with a dynamic programming approach where


In [ ]:
# Dynamic programming implementation of LCS problem
 
# Returns length of LCS for X[0..m-1], Y[0..n-1] 
def lcs(X, Y, m, n):
    L = [[0 for x in xrange(n+1)] for x in xrange(m+1)]
 
    # Following steps build L[m+1][n+1] in bottom up fashion. Note
    # that L[i][j] contains length of LCS of X[0..i-1] and Y[0..j-1] 
    for i in xrange(m+1):
        for j in xrange(n+1):
            if i == 0 or j == 0:
                L[i][j] = 0
            elif X[i-1] == Y[j-1]:
                L[i][j] = L[i-1][j-1] + 1
            else:
                L[i][j] = max(L[i-1][j], L[i][j-1])
 
    # Following code is used to print LCS
    index = L[m][n]
 
    # Create a character array to store the lcs string
    lcs = [""] * (index+1)
    lcs[index] = "\0"
 
    # Start from the right-most-bottom-most corner and
    # one by one store characters in lcs[]
    i = m
    j = n
    while i > 0 and j > 0:
 
        # If current character in X[] and Y are same, then
        # current character is part of LCS
        if X[i-1] == Y[j-1]:
            lcs[index-1] = X[i-1]
            i-=1
            j-=1
            index-=1
 
        # If not same, then find the larger of two and
        # go in the direction of larger value
        elif L[i-1][j] > L[i][j-1]:
            i-=1
        else:
            j-=1
 
    print "LCS of " + X + " and " + Y + " is " + "".join(lcs) 
 
# Driver program
X = "AGGTAB"
Y = "GXTXAYB"
m = len(X)
n = len(Y)
lcs(X, Y, m, n)

In [ ]:
passed in a list of dictionaries
also passed a character
passed single characted to int
if a character does not exist in the dict
then the defualt value it zero 

find the highest possisble value for a character
in the dicts

now design it to take an abatrary operator and reutrn 
the highest value based on the operator

and then have it return ascending and descending order

Time Travelling dictionary

Design a time traveling dictionary, has a get and put function where the get function takes a time and returns the corresponding value at the time.


In [47]:
import time
import math

class TimeTravelDict:
    def __init__(self):
        self.dict = {}
    
    def get(self, key, time):
        if not self.dict[key]:
            return -1
        most_recent, value = math.inf, None
        for a, b in self.dict[key]:
            if b < time:
                if (time - b) < most_recent:
                    most_recent = b
                    value = a
        
        if value == None:
            return -1
        else:
            return value
        
    def put(self, key, value):
        if not key in self.dict:
            self.dict[key] = [(value, time.time())]
        self.dict[key].append((value, time.time()))
        print(self.dict[key])
               
tt = TimeTravelDict()
tt.put('a', 11)
tt.put('a', 12)
tt.put('a', 13)
tt.put('a', 14)

tt.get('a', 1513571590.2447577)


[(11, 1513571674.8668716), (11, 1513571674.8668728)]
[(11, 1513571674.8668716), (11, 1513571674.8668728), (12, 1513571674.8670583)]
[(11, 1513571674.8668716), (11, 1513571674.8668728), (12, 1513571674.8670583), (13, 1513571674.8671353)]
[(11, 1513571674.8668716), (11, 1513571674.8668728), (12, 1513571674.8670583), (13, 1513571674.8671353), (14, 1513571674.8672113)]
Out[47]:
-1

Alien Dictionary

Given a sorted dictionary of an alien language, find order of characters

Input:  words[] = {"baa", "abcd", "abca", "cab", "cad"}
Output: Order of characters is 'b', 'd', 'a', 'c'
Note that words are sorted and in the given language "baa" 
comes before "abcd", therefore 'b' is before 'a' in output.
Similarly we can find other orders.

Input:  words[] = {"caa", "aaa", "aab"}
Output: Order of characters is 'c', 'a', 'b'

The idea is to create a graph of characters a then find topological sorting of the graph.

  1. Create a graph g with number of vertices equal to the size of alphabet in the given language. For example, if the alphabet size is 5, then there can be 5 characters in words. Initially there are no edges in graph.
  2. DO the following for every pair of adjacent words in given sorted array.
    1. Let the current pair of words be word1 and word2. One by one compare characters of both words and find the mismatching characters.
    2. Create an edge in g from mismatching character of word1 to that of word2.
  3. Print topological sorting of the above created graph.

In [48]:
#[2::][1::2]
import collections

words = ["baa", "", "abcd", "abca", "cab", "cad"]

def alienOrder(words):
    pre, suc = collections.defaultdict(set), collections.defaultdict(set)
    for pair in zip(words, words[1:]):
        print(pair)
        for a, b in zip(*pair):
            if a != b:
                suc[a].add(b)
                pre[b].add(a)
                break
    print('succ %s' % suc)
    print('pred %s' % pre)
    chars = set(''.join(words))
    print('chars %s' % chars)
    print(set(pre))
    free = chars - set(pre)
    print('free %s' % free)
    order = ''
    while free:
        a = free.pop()
        order += a
        for b in suc[a]:
            pre[b].discard(a)
            if not pre[b]:
                free.add(b)
    
    if set(order) == chars:
        return order 
    else:
        False
#     return order * (set(order) == chars)
    
alienOrder(words)


('baa', '')
('', 'abcd')
('abcd', 'abca')
('abca', 'cab')
('cab', 'cad')
succ defaultdict(<class 'set'>, {'a': {'c'}, 'd': {'a'}, 'b': {'d'}})
pred defaultdict(<class 'set'>, {'d': {'b'}, 'a': {'d'}, 'c': {'a'}})
chars {'b', 'a', 'c', 'd'}
{'d', 'a', 'c'}
free {'b'}
Out[48]:
'bdac'

Binary Search


In [ ]:
def binarySearch(alist, value):
    mini = 0
    maxi = len(alist)
    
    while mini <= maxi:
        print('here')
        pivot = (maxi - mini) // 2
        current_value = alist[pivot]
        
        if current_value < value:
            mini = pivot + 1
        elif current_value > value:
            maxi = pivot -1
        else:
            return pivot 
    
    return pivot or -1
          
    
    
test1 = [0, 5, 10 , 23, 46, 49, 78]
test2 = [0, 5, 10]
test3 = [0]
print(binarySearch(test1, 49))
print(binarySearch(test2, 10))
binarySearch(test3, 90)