You are given a list of sorted arrays, arrays
, where each array contains distinct integers sorted in ascending order. Your task is to find the longest common subsequence that exists in all of the given arrays. A subsequence of an array is a sequence that can be derived from the array by deleting some or no elements without changing the order of the remaining elements.
For example, consider the following input:
arrays = [
[1, 3, 4, 5, 6],
[2, 3, 5, 6],
[1, 2, 3, 6]
]
The longest common subsequence in this case is [3, 6]
because both 3 and 6 appear in all three arrays, and no other subsequence longer than 2 exists.
Here are some additional examples to consider:
arrays = [[1, 2, 3], [4, 5, 6]]
(Output: []
)arrays = [[1, 2, 3, 4, 5], [1, 2, 5, 7, 9], [1, 3, 4, 5, 8]]
(Output: [1, 5]
)arrays = [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
(Output: [1, 2, 3]
)Write a function that takes a list of sorted arrays as input and returns the longest common subsequence as a sorted list of integers. Ensure that the solution is efficient and handles edge cases gracefully, such as empty input arrays or arrays with no common subsequence.
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force approach for finding the longest common subsequence between sorted arrays involves checking every possible subsequence. We essentially explore all combinations of elements from the arrays to see if they form a common sequence. We then find the longest among these common sequences.
Here's how the algorithm would work step-by-step:
def longest_common_subsequence_brute_force(arrays):
if not arrays:
return []
first_array = arrays[0]
all_common_subsequences = []
def is_subsequence(sequence, array):
array_index = 0
sequence_index = 0
while array_index < len(array) and sequence_index < len(sequence):
if array[array_index] == sequence[sequence_index]:
sequence_index += 1
array_index += 1
return sequence_index == len(sequence)
def generate_subsequences(array, index, current_subsequence):
# Base case: When we have processed all elements of the array
if index == len(array):
if current_subsequence:
is_common = True
for other_array in arrays[1:]:
if not is_subsequence(current_subsequence, other_array):
is_common = False
break
# Add to common subsequences if valid
if is_common:
all_common_subsequences.append(current_subsequence)
return
# Recursive case 1: Exclude the current element
generate_subsequences(array, index + 1, current_subsequence)
# Recursive case 2: Include the current element
generate_subsequences(array, index + 1, current_subsequence + [array[index]])
generate_subsequences(first_array, 0, [])
longest_subsequence = []
for subsequence in all_common_subsequences:
# Find the maximum common subsequence.
if len(subsequence) > len(longest_subsequence):
longest_subsequence = subsequence
return longest_subsequence
The most efficient way to find the longest common subsequence is to focus on elements that appear in all input sets. By iterating through the sets simultaneously, we can quickly identify and extend any common subsequence, avoiding unnecessary comparisons. This method significantly reduces the number of checks required compared to brute-force approaches.
Here's how the algorithm would work step-by-step:
def longest_common_subsequence_between_sorted_arrays(arrays):
if not arrays:
return []
array_indices = [0] * len(arrays)
longest_common_subsequence = []
while True:
# Get the current elements from each array.
current_elements = []
for i in range(len(arrays)):
if array_indices[i] < len(arrays[i]):
current_elements.append(arrays[i][array_indices[i]])
else:
# If any array is exhausted, stop.
return longest_common_subsequence
# Check if all elements are equal.
if all(element == current_elements[0] for element in current_elements):
# Found a common element, add to subsequence.
longest_common_subsequence.append(current_elements[0])
for i in range(len(arrays)):
array_indices[i] += 1
else:
# Find the smallest element to advance.
minimum_element = min(current_elements)
# Advance the arrays with the smallest element.
for i in range(len(arrays)):
if arrays[i][array_indices[i]] == minimum_element:
# Smallest element cannot be in LCS
array_indices[i] += 1
return longest_common_subsequence
Case | How to Handle |
---|---|
One or more input arrays are null or empty | Return an empty list, as no common subsequence is possible. |
All input arrays are identical and contain the same single element. | Return a list containing that single element. |
Input arrays have no common elements. | Return an empty list, indicating no common subsequence exists. |
The length of the shortest array is very small (e.g., 1 or 2) | The algorithm should still function correctly and efficiently even with small array sizes, although the common sequence will be short. |
Input arrays are extremely large, potentially exceeding available memory during intermediate calculations or data structures. | Consider using a memory-efficient approach, such as comparing arrays in smaller chunks or using iterators to avoid loading the entire dataset into memory at once. |
Input arrays contain large integer values that could lead to integer overflow during calculations (e.g., summing indices). | Use appropriate data types (e.g., long) to store intermediate results, or consider using modular arithmetic if the overflow is acceptable. |
The common subsequence is very long, approaching the size of the smallest input array. | Ensure the data structure used to store the common subsequence can handle this length efficiently without performance degradation. |
Input arrays contain duplicate values within the array, but some of these duplicate values are part of the longest common subsequence. | The algorithm should correctly identify and include only the appropriate instances of the duplicate values to form the longest common subsequence. |