
Problem Statement
The task involves finding the longest common subsequence shared among a list of sorted integer arrays, each listed in strictly increasing order. A subsequence refers to a sequence that can be derived from another by deleting individual elements without altering the remaining sequence's order. The main challenge is to identify the common elements across all provided arrays that exhibit the maximum sequential integrity without reordering or additional insertions.
Examples
Example 1
Input:
arrays = [[1,3,4], [1,4,7,9]]
Output:
[1,4]
Explanation:
The longest common subsequence in the two arrays is [1,4].
Example 2
Input:
arrays = [[2,3,6,8], [1,2,3,5,6,7,10], [2,3,4,6,9]]
Output:
[2,3,6]
Explanation:
The longest common subsequence in all three arrays is [2,3,6].
Example 3
Input:
arrays = [[1,2,3,4,5], [6,7,8]]
Output:
[]
Explanation:
There is no common subsequence between the two arrays.
Constraints
2 <= arrays.length <= 100
1 <= arrays[i].length <= 100
1 <= arrays[i][j] <= 100
arrays[i]
is sorted in strictly increasing order.
Approach and Intuition
When presented with multiple sorted arrays, the objective is to determine a series of numbers that appears in the same ordered sequence across all the arrays. Here is a step-by-step approach to understand how the problem can be approached:
- First, understand the nature of a subsequence. Unlike substrings or subarrays, subsequences are not required to be contiguous. They only need to maintain the order of appearance.
- Begin with checking the common elements. Since all arrays are sorted, an efficient pointer or binary search can significantly speed up comparisons across these arrays.
- Use a multi-pointer technique where each array is iterated through simultaneously to check for common elements. This can also be optimized using a hashing mechanism for each array, where the counts of each element are stored.
- The idea is then to move through each array by increasing the pointer position when a common element is found across all arrays, thereby building the longest common subsequence incrementally.
- Given the constraints:
- There could be up to 100 arrays, each with as many as 100 integers, so solutions need to be optimized to handle potentially large inputs efficiently.
- The time complexity ideally should be linear with respect to the total number of elements across all arrays, considering efficient traversals and checks.
- Edge cases include:
- Arrays with no common elements, leading to an empty subsequence as shown in the examples.
- Arrays of different lengths, where the algorithm must efficiently skip unmatched numbers using the sorted order to its advantage.
The examples clearly illustrate these concepts where:
- In Example 1, arrays [[1,3,4], [1,4,7,9]] share '1' and '4' as a common subsequence.
- In Example 2, arrays have multiple common subsequences, and '2', '3', '6' form the longest across all given arrays.
- Example 3 shows an edge case where the arrays do not have any common elements, resulting in an empty array as output.
Solutions
- C++
- Java
- Python
class Solution {
public:
vector<int> findLongestCommonSubsequence(vector<vector<int>>& listOfArrays) {
vector<int> minArray = listOfArrays[0];
for (const auto& arr : listOfArrays) {
if (arr.size() < minArray.size()) {
minArray = arr;
}
}
vector<int> commonSubsequence(minArray.begin(), minArray.end());
for (const auto& arr : listOfArrays) {
if (commonSubsequence.empty()) {
return commonSubsequence;
}
vector<int> excludedElements;
for (int value : commonSubsequence) {
if (!performBinarySearch(value, arr)) {
excludedElements.push_back(value);
}
}
for (int value : excludedElements) {
commonSubsequence.erase(
remove(commonSubsequence.begin(), commonSubsequence.end(), value),
commonSubsequence.end());
}
}
return commonSubsequence;
}
private:
bool performBinarySearch(int target, const vector<int>& container) {
int low = 0;
int high = container.size() - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (container[mid] > target) {
high = mid - 1;
} else if (container[mid] < target) {
low = mid + 1;
} else {
return true;
}
}
return false;
}
};
In this solution for finding the longest common subsequence between sorted arrays using C++, the approach involves a combination of iterative and binary search techniques. Follow the outlined steps to understand how the solution works:
Begin by initializing
minArray
with the first array from the given list of arrays. This step determines the shortest array in the list as a potential candidate for the longest common subsequence.Iterate through each array in the list and update
minArray
if a shorter array is found. This reduces the initial search space.Copy
minArray
intocommonSubsequence
, which serves as a temporary storage for values that are being evaluated for the common subsequence.Run a nested loop where the outer loop traverses through each array in
listOfArrays
and the inner loop goes through each element incommonSubsequence
:For each value in
commonSubsequence
, check if it exists in the current array using the helper functionperformBinarySearch
.If a value is not found, add it to
excludedElements
.After checking all values in
commonSubsequence
for the current array, remove all the values that are inexcludedElements
using theremove
function.
The binary search function
performBinarySearch
efficiently checks for the presence of an element within a sorted array. It adjusts search boundaries (low
andhigh
) based on comparisons, and returnstrue
if the target is found.
At the end of these operations, commonSubsequence
will contain only those elements present in every array within listOfArrays
. This method minimizes unnecessary checks and ensures that the execution remains efficient by leveraging the sorted nature of the arrays.
class Solution {
public List<Integer> findLongestCommonSubsequence(int[][] arrays) {
int[] minArray = arrays[0];
for (int[] arr : arrays) {
if (arr.length < minArray.length) {
minArray = arr;
}
}
List<Integer> commonElements = new ArrayList<>();
for (int element : minArray) {
commonElements.add(element);
}
for (int[] arr : arrays) {
if (commonElements.isEmpty()) {
return commonElements;
}
List<Integer> removeList = new ArrayList<>();
for (Integer element : commonElements) {
if (!binarySearchInArray(element, arr)) removeList.add(element);
}
commonElements.removeAll(removeList);
}
return commonElements;
}
private boolean binarySearchInArray(int targetElement, int[] array) {
int left = 0;
int right = array.length - 1;
while (left <= right) {
int mid = (right + left) / 2;
if (array[mid] > targetElement) {
right = mid - 1;
} else if (array[mid] < targetElement) {
left = mid + 1;
} else {
return true;
}
}
return false;
}
}
This Java code presents a method for finding the longest common subsequence among several sorted arrays. Achieve this using a practical and efficient approach outlined below:
Start by identifying which among the given arrays is the shortest. This determination uses simple iteration, comparing lengths, with the assumption that a shorter array may limit the maximum possible size of the common subsequence.
Initiate tracking for common elements with a dynamic list initialized to contain all elements of the identified shortest array.
Then, iterate over each array included in the input. During each iteration:
- Maintain an additional list to keep track of elements to be removed from potential common subsequences because they do not appear in the current array being checked.
- Utilize a binary search, given the arrays are already sorted, to check the existence of each element efficiently. This check is crucial and helps prune out non-common elements quickly.
If common candidates are found to be absent in any array, the method terminates early and returns an empty list as no common elements exist among all arrays.
After iterating through all arrays, the remaining elements in the list of common elements represent the longest common subsequence. This list is then returned as the result of the function.
This code leverages binary search to enhance the efficiency of checking for commonality, making this solution particularly suitable for handling large data sets where brute force methods would be computationally expensive.
class Solution:
def largestSharedElements(self, lists: List[List[int]]) -> List[int]:
def contains(element, sequence):
start = 0
end = len(sequence) - 1
while start <= end:
middle = start + (end - start) // 2
if sequence[middle] > element:
end = middle - 1
elif sequence[middle] < element:
start = middle + 1
else:
return True
return False
minList = lists[0]
for lst in lists:
if len(lst) < len(minList):
minList = lst
common_elements = minList
for lst in lists:
if len(common_elements) == 0:
return common_elements
notCommon = []
for item in common_elements:
if not contains(item, lst):
notCommon.append(item)
for item in notCommon:
common_elements.remove(item)
return common_elements
The following Python solution identifies the longest common subsequence shared across sorted arrays. The core approach checks commonalities across all provided arrays using binary search and list manipulation strategies. Below outline crucial steps involved in this algorithm:
- Define an internal function
contains
to perform binary search, determining if an element exists in a sorted sequence. - Iterate through the list of lists to detect the smallest list by length, referred to as
minList
. This smallest list serves as the baseline for comparison with all other arrays. - Initialize
common_elements
withminList
's elements, which will be refined to only hold the common elements as comparisons are made. - For each list in the provided list of lists, use a nested loop to scrutinize each element in
common_elements
:- If an element in
common_elements
is not found in the current list using thecontains
function, append it tonotCommon
. - Refine
common_elements
by removing elements listed innotCommon
.
- If an element in
- If at any iteration
common_elements
becomes empty, the process aborts as no common elements exist across all lists, returning an empty list. - After all comparisons,
common_elements
will hold only the elements common across all provided lists.
This method's efficiency stems from leveraging binary search within each array, substantially reducing the computational overhead compared to brute-force approaches. This solution works optimally with sorted arrays, ensuring accuracy and performance.
No comments yet.