Longest Common Subsequence Between Sorted Arrays

Updated on 05 June, 2025
Longest Common Subsequence Between Sorted Arrays header image

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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
cpp
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:

  1. 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.

  2. Iterate through each array in the list and update minArray if a shorter array is found. This reduces the initial search space.

  3. Copy minArray into commonSubsequence, which serves as a temporary storage for values that are being evaluated for the common subsequence.

  4. Run a nested loop where the outer loop traverses through each array in listOfArrays and the inner loop goes through each element in commonSubsequence:

    • For each value in commonSubsequence, check if it exists in the current array using the helper function performBinarySearch.

    • 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 in excludedElements using the remove function.

  5. The binary search function performBinarySearch efficiently checks for the presence of an element within a sorted array. It adjusts search boundaries (low and high) based on comparisons, and returns true 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.

java
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:

  1. 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.

  2. Initiate tracking for common elements with a dynamic list initialized to contain all elements of the identified shortest array.

  3. 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.
  4. 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.

  5. 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.

python
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:

  1. Define an internal function contains to perform binary search, determining if an element exists in a sorted sequence.
  2. 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.
  3. Initialize common_elements with minList's elements, which will be refined to only hold the common elements as comparisons are made.
  4. 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 the contains function, append it to notCommon.
    • Refine common_elements by removing elements listed in notCommon.
  5. If at any iteration common_elements becomes empty, the process aborts as no common elements exist across all lists, returning an empty list.
  6. 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.

Comments

No comments yet.