Solving Leetcode Interviews in Seconds with AI: Frequencies of Shortest Supersequences
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_permutationfunction. 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.
- Optimization: The code avoids adding SCS permutations using the
- 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)