Skip to main content

Command Palette

Search for a command to run...

Solving Leetcode Interviews in Seconds with AI: Frequencies of Shortest Supersequences

Updated
5 min read

Introduction

In this blog post, we will explore how to solve the LeetCode problem "3435" using AI. LeetCode is a popular platform for preparing for coding interviews, and with the help of AI tools like Chatmagic, we can generate solutions quickly and efficiently - helping you pass the interviews and get the job offer without having to study for months.

Problem Statement

You are given an array of strings words. Find all shortest common supersequences (SCS) of words that are not permutations of each other. A shortest common supersequence is a string of minimum length that contains each string in words as a subsequence. Return a 2D array of integers freqs that represent all the SCSs. Each freqs[i] is an array of size 26, representing the frequency of each letter in the lowercase English alphabet for a single SCS. You may return the frequency arrays in any order. Example 1: Input: words = ["ab","ba"] Output: [[1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]] Explanation: The two SCSs are "aba" and "bab". The output is the letter frequencies for each one. Example 2: Input: words = ["aa","ac"] Output: [[2,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]] Explanation: The two SCSs are "aac" and "aca". Since they are permutations of each other, keep only "aac". Example 3: Input: words = ["aa","bb","cc"] Output: [[2,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]] Explanation: "aabbcc" and all its permutations are SCSs. Constraints: 1 <= words.length <= 256 words[i].length == 2 All strings in words will altogether be composed of no more than 16 unique lowercase letters. All strings in words are unique.

Explanation

  • Core Idea: Dynamic programming is used to explore all possible supersequences. A bitmask is employed to track which words have been covered as subsequences. The DP state includes the last character added and the mask of covered words.
    • Optimization: The code avoids adding SCS permutations using the check_permutation function. It only adds frequencies if no existing frequency vector is a permutation of the current frequency vector.
    • Frequency Counting: Efficiently counts character frequencies for each SCS, creating a 26-element array for each.
  • Runtime Complexity: O(2n m L), where n is the number of words, m is the number of unique characters (up to 16), and L is the average length of the supersequences. Storage Complexity: O(2n * m).
def solve():
    words = ["ab", "ba"]
    # words = ["aa", "ac"]
    # words = ["aa", "bb", "cc"]
    # words = ["abc", "bca", "cab"]
    # words = ["abcd", "bcda", "cdab", "dabc"]

    def find_scs_freqs(words):
        n = len(words)
        unique_chars = set()
        for word in words:
            unique_chars.update(set(word))
        unique_chars = sorted(list(unique_chars))
        char_to_index = {char: i for i, char in enumerate(unique_chars)}

        memo = {}  # (mask, last_char) -> (length, set of frequencies)

        def get_freq(s):
            freq = [0] * 26
            for char in s:
                freq[ord(char) - ord('a')] += 1
            return freq

        def check_permutation(freq1, freq2):
            return sorted(freq1) == sorted(freq2)

        def dp(mask, last_char):
            if mask == (1 << n) - 1:
                return 0, {tuple()}  # Base case: all words are subsequences

            if (mask, last_char) in memo:
                return memo[(mask, last_char)]

            min_len = float('inf')
            freqs = set()

            for char in unique_chars:
                new_mask = mask
                for i in range(n):
                    if (mask >> i) & 1 == 0:  # If word i is not yet a subsequence
                        if char in words[i] and is_subsequence_after_char(char, words[i], last_char):
                            new_mask |= (1 << i)

                len_without_char, freqs_without_char = dp(new_mask, char)
                curr_len = len_without_char + 1

                if curr_len < min_len:
                    min_len = curr_len
                    freqs = set()
                    for freq_tuple in freqs_without_char:
                        new_freq = [0] * 26
                        if len(freq_tuple) != 0:
                          new_freq = list(freq_tuple)
                        new_freq[ord(char) - ord('a')] += 1

                        is_permutation_present = False
                        for existing_freq in freqs:
                            if check_permutation(new_freq, list(existing_freq)):
                                is_permutation_present = True
                                break

                        if not is_permutation_present:
                          freqs.add(tuple(new_freq))


                elif curr_len == min_len:
                     for freq_tuple in freqs_without_char:
                        new_freq = [0] * 26
                        if len(freq_tuple) != 0:
                          new_freq = list(freq_tuple)
                        new_freq[ord(char) - ord('a')] += 1

                        is_permutation_present = False
                        for existing_freq in freqs:
                            if check_permutation(new_freq, list(existing_freq)):
                                is_permutation_present = True
                                break
                        if not is_permutation_present:
                          freqs.add(tuple(new_freq))

            memo[(mask, last_char)] = (min_len, freqs)
            return min_len, freqs

        def is_subsequence_after_char(char, word, last_char):
            if last_char is None:
                return True
            last_char_index = -1
            for i, c in enumerate(word):
                if c == last_char:
                    last_char_index = i
            if last_char_index == -1:
                return True
            for i in range(last_char_index + 1, len(word)):
                if word[i] == char:
                    return True
            return False


        _, all_freqs = dp(0, None)
        result = [list(freq) for freq in all_freqs]

        return result

    result = find_scs_freqs(words)
    print(result)


    # Code
    ```python
    def solve():    words = ["ab", "ba"]
    # words = ["aa", "ac"]
    # words = ["aa", "bb", "cc"]
    # words = ["abc", "bca", "cab"]
    # words = ["abcd", "bcda", "cdab", "dabc"]

    def find_scs_freqs(words):
        n = len(words)
        unique_chars = set()
        for word in words:
            unique_chars.update(set(word))
        unique_chars = sorted(list(unique_chars))
        char_to_index = {char: i for i, char in enumerate(unique_chars)}

        memo = {}  # (mask, last_char) -> (length, set of frequencies)

        def get_freq(s):
            freq = [0] * 26
            for char in s:
                freq[ord(char) - ord('a')] += 1
            return freq

        def check_permutation(freq1, freq2):
            return sorted(freq1) == sorted(freq2)

        def dp(mask, last_char):
            if mask == (1 << n) - 1:
                return 0, {tuple()}  # Base case: all words are subsequences

            if (mask, last_char) in memo:
                return memo[(mask, last_char)]

            min_len = float('inf')
            freqs = set()

            for char in unique_chars:
                new_mask = mask
                for i in range(n):
                    if (mask >> i) & 1 == 0:  # If word i is not yet a subsequence
                        if char in words[i] and is_subsequence_after_char(char, words[i], last_char):
                            new_mask |= (1 << i)

                len_without_char, freqs_without_char = dp(new_mask, char)
                curr_len = len_without_char + 1

                if curr_len < min_len:
                    min_len = curr_len
                    freqs = set()
                    for freq_tuple in freqs_without_char:
                        new_freq = [0] * 26
                        if len(freq_tuple) != 0:
                          new_freq = list(freq_tuple)
                        new_freq[ord(char) - ord('a')] += 1

                        is_permutation_present = False
                        for existing_freq in freqs:
                            if check_permutation(new_freq, list(existing_freq)):
                                is_permutation_present = True
                                break

                        if not is_permutation_present:
                          freqs.add(tuple(new_freq))


                elif curr_len == min_len:
                     for freq_tuple in freqs_without_char:
                        new_freq = [0] * 26
                        if len(freq_tuple) != 0:
                          new_freq = list(freq_tuple)
                        new_freq[ord(char) - ord('a')] += 1

                        is_permutation_present = False
                        for existing_freq in freqs:
                            if check_permutation(new_freq, list(existing_freq)):
                                is_permutation_present = True
                                break
                        if not is_permutation_present:
                          freqs.add(tuple(new_freq))

            memo[(mask, last_char)] = (min_len, freqs)
            return min_len, freqs

        def is_subsequence_after_char(char, word, last_char):
            if last_char is None:
                return True
            last_char_index = -1
            for i, c in enumerate(word):
                if c == last_char:
                    last_char_index = i
            if last_char_index == -1:
                return True
            for i in range(last_char_index + 1, len(word)):
                if word[i] == char:
                    return True
            return False


        _, all_freqs = dp(0, None)
        result = [list(freq) for freq in all_freqs]

        return result

    result = find_scs_freqs(words)
    print(result)

More from this blog

C

Chatmagic blog

2894 posts