Permutations

"Given a set of distinct numbers, find all of its permutations."

Just recently I got stuck on this problem and ended up using a brute force technique that was not only inefficient, did not entirely solve the problem.

I found at least two ways to solve this properly (Grokking the Coding Interview: Patterns for Coding Questions .. (n.d.) One way is far superior based on time complexity. Let's begin with the less efficient pattern first:

Subsets Pattern with Time Complexity of O(N*N!)

# subsets_str_perm.py
#   Given a set of distinct letters, find all of its permutations.
# by: Scott Gordon

from collections import deque


def get_permutation(string):
    string_length = len(string)
    result = []
    permutations = deque()
    permutations.append([])
    for current_letter in string:
        # Take all permutations and add current num to make new permutation
        s = len(permutations)
        for _ in range(s):
            old_permutation = permutations.popleft()
            # Create new permutation by adding current num @ every position
            for j in range(len(old_permutation) + 1):
                new_permutation = list(old_permutation)
                new_permutation.insert(j, current_letter)
                if len(new_permutation) == string_length:
                    result.append(new_permutation)
                else:
                    permutations.append(new_permutation)

    return result


def find_permutation(string, pattern):
    permuted_pattern = get_permutation(pattern)
    for substring in permuted_pattern:
        s = "".join(substring)
        if s in string:
            result = True
            break
        else:
            result = False
    return result


def main():
    print("Permutation exist: " + str(find_permutation("oidbcaf", "abc")))
    print("Permutation exist: " + str(find_permutation("odicf", "dc")))
    print("Permutation exist: " + str(find_permutation("bcdxabcdy", "bcdyabcdx")))
    print("Permutation exist: " + str(find_permutation("aaacb", "abc")))


main()

Next let's take a look at the more efficient pattern:

Sliding Window Pattern with Time Complexity of O(N)

# sliding_win_str_perm.py
#    Given a set of distinct numbers, find all of its permutations
# by: Scott Gordon


def find_permutation(str1, pattern):
    window_start, matched = 0, 0
    char_frequency = {}
    # Create a HashMap to calculate frequencies of all chars in pattern.
    for chr in pattern:
        if chr not in char_frequency:
            char_frequency[chr] = 0
        char_frequency[chr] += 1

    # Iterate through string, add one char at a time to sliding window.
    for window_end in range(len(str1)):
        right_char = str1[window_end]
        if right_char in char_frequency:
            # If added char matches HashMap char, decrement map frequency. If
            # char frequency zero, complete match.
            char_frequency[right_char] -= 1
            if char_frequency[right_char] == 0:
                matched += 1

        # If num of char matched equal to num of distinct chars in
        # pattern (total chars in HashMap), required permutation
        # achieved.
        if matched == len(char_frequency):
            return True

        # If win size > len of pattern, shrink win to make == pattern size.
        # If outgoing char part of pattern, put back in frequency HashMap.
        if window_end >= len(pattern) - 1:
            left_char = str1[window_start]
            window_start += 1
            if left_char in char_frequency:
                if char_frequency[left_char] == 0:
                    matched -= 1
                char_frequency[left_char] += 1

    return False


def main():
    print("Permutation exist: " + str(find_permutation("oidbcaf", "abc")))
    print("Permutation exist: " + str(find_permutation("odicf", "dc")))
    print("Permutation exist: " + str(find_permutation("bcdxabcdy", "bcdyabcdx")))
    print("Permutation exist: " + str(find_permutation("aaacb", "abc")))


main()

So, I went from brute forcing the algorithm in my original context, to solving the problem using the subsets pattern, then to improve upon its time complexity using the sliding window pattern. I would recommend running these patterns and debugging them over and over again if this is challenging, I know I had to.

References:

Grokking the Coding Interview: Patterns for Coding Questions .. (n.d.). www.educative.Io. Retrieved October 3, 2021, from https://www.educative.io/courses/grokking-the-coding-interview

17