Open In App

Length of longest common subarray for given N arrays

Last Updated : 25 Jan, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given a 2-D array array[][] containing N arrays, the task is to find the longest common subarray(LCS) among N arrays.

Examples:

Input: N = 3, 
array[][] = { { 0, 1, 2, 3, 4 }, 
{ 2, 3, 4 }, 
{ 4, 0, 1, 2, 3 } }
Output: 2
Explanation: The longest common subpath is {2, 3}.

Input: N = 2, 
array[][] = {{0, 1, 2, 3, 4}, 
{4, 3, 2, 1, 0}}
Output: 1
Explanation: The possible longest common subpaths are [0], [1], [2], [3], and [4]. All have a length of 1.

 

Approach: It is clear that the length of the LCS can be binary search. That is, if there is a common sub-array with length L, there will always be a common sub-array with a length less than L. Therefore, the binary search framework is as follows:

lower = 0, upper = maxlength + 1; // LCS in [lower, upper).
while (lower + 1 < upper) {
   middle = (lower + upper) / 2;
   if (there is some common substring with length middle) {
       lower = middle;
   } else{
       upper = middle;
   }
}
LCS = lower;

So, the key point here is to check whether there are some common sub-arrays with length middle. A usual way is to adopt Hashing i.e, Rabin Karp Hashing.

Hash(S[0..n-1]) = (S[n-1] * MAGIC^0 + S[n-2] * MAGIC^1 + .. + S[n-1-i] * MAGIC^i + … ) % MOD

The most convenient point here is that Hash(S[0…i]) can be used to calculate the Hash(S[l…r]) in O(1) time, with O(N) preparation. That is,

Hash(S[l..r]) = (Hash(S[0..r]) – Hash(S[0..l-1]) * MAGIC^(r-l+1)) % MOD

Therefore, all hash values of sub-array with length middle from two given arrays can be found, and then, check whether there is any overlap. This procedure can be done via Hash Table in O(|S|) or Set (Balanced Binary Search Tree) in O(|S|log|S|). Therefore, Binary Search + Hash can solve this problem in O(|S| log|S|) time. Follow the steps below to solve this problem:

  • Initialize the variable min_len as maximum length possible i.e, INT_MAX.
  • Iterate over the range [0, N) using the variable i and perform the following tasks:
    • Set the value of min_len as the minimum of min_len or array[i].size().
  • Initialize the variables start as 0, end as min_len, and mid as 0 to perform the binary search on the length.
  • Traverse over the while loop till start is less than equal to end and perform the following steps:
    • Set the value of mid as the average of start and end.
    • Call the function check(array, mid) to check if the length mid is possible as the answer or not using Rabin-karp hashing.
    • If the function returns true, then set the value of start as mid+1 otherwise end as mid-1.
  • After performing the above steps, print the value of end as the answer.

Below is the implementation of the above approach

C++




// C++ program for the above approach
#include <bits/stdc++.h>
using namespace std;
 
const long long p = 1299827;
const long long mod = 1e11 + 7;
long long M;
 
// Function to implement rabin - carp
// hashing to check whether the given length
// is possible or not
bool check(vector<vector<int> >& array, int len)
{
    if (len == 0)
        return true;
    map<long long, int> freq;
    for (int i = 0; i < M; i++) {
        long long curr_hash = 0, pow = 1;
        set<long long> found_hashes;
        for (int j = 0; j < len; j++) {
            curr_hash = (curr_hash * p) % mod;
            curr_hash += array[i][j];
            if (j != len - 1)
                pow = (pow * p) % mod;
        }
        found_hashes.insert(curr_hash);
        for (int j = len; j < array[i].size(); j++) {
            curr_hash += mod;
            curr_hash -= (array[i][j - len] * pow) % mod;
            curr_hash %= mod;
            curr_hash = curr_hash * p;
            curr_hash %= mod;
            curr_hash += array[i][j];
            curr_hash %= mod;
            found_hashes.insert(curr_hash);
        }
        while (found_hashes.size()) {
            long long h = *(found_hashes.begin());
            found_hashes.erase(found_hashes.begin());
            freq[h]++;
            if (freq[h] == M)
                return true;
        }
    }
    return false;
}
 
// Function to find the longest common sub-array
// from the given N arrays
int longestCommonSubpath(long long N,
                         vector<vector<int> >& array)
{
 
    M = N;
 
    // Find the maximum length possible
    int minlen = INT_MAX;
    for (int i = 0; i < array.size(); i++) {
        minlen = min(minlen, (int)array[i].size());
    }
 
    // Binary search on the length
    int start = 0, end = minlen, mid = 0;
    while (start <= end) {
        int mid = (start + end) / 2;
 
        // Function Call to check whether
        // it is possible or not
        if (check(array, mid)) {
            start = mid + 1;
        }
        else {
            end = mid - 1;
        }
    }
    return end;
}
 
// Driver Code
int main()
{
    vector<vector<int> > arr{ { 0, 1, 2, 3, 4 },
                              { 2, 3, 4 },
                              { 4, 0, 1, 2, 3 } };
 
    long long N = arr.size();
 
    cout << longestCommonSubpath(N, arr);
 
    return 0;
}


Java




// Java program for the above approach
import java.util.HashMap;
import java.util.HashSet;
 
class GFG {
 
  static long p = 1299827;
  static long mod = (long) 1E11 + 7;
  static long M;
 
  // Function to implement rabin - carp
  // hashing to check whether the given length
  // is possible or not
  static boolean check(int[][] array, int len) {
    if (len == 0)
      return true;
    HashMap<Long, Integer> freq = new HashMap<Long, Integer>();
    for (int i = 0; i < M; i++) {
      long curr_hash = 0, pow = 1;
      HashSet<Long> found_hashes = new HashSet<Long>();
      for (int j = 0; j < len; j++) {
        curr_hash = (curr_hash * p) % mod;
        curr_hash += array[i][j];
        if (j != len - 1)
          pow = (pow * p) % mod;
      }
      found_hashes.add(curr_hash);
      for (int j = len; j < array[i].length; j++) {
        curr_hash += mod;
        curr_hash -= (array[i][j - len] * pow) % mod;
        curr_hash %= mod;
        curr_hash = curr_hash * p;
        curr_hash %= mod;
        curr_hash += array[i][j];
        curr_hash %= mod;
        found_hashes.add(curr_hash);
      }
      while (found_hashes.size() > 0) {
        long h = found_hashes.iterator().next();
        found_hashes.remove(h);
        if (freq.containsKey(h)) {
          freq.put(h, freq.get(h) + 1);
        } else {
          freq.put(h, 1);
        }
        if (freq.get(h) == M)
          return true;
      }
    }
    return false;
  }
 
  // Function to find the longest common sub-array
  // from the given N arrays
  public static int longestCommonSubpath(long N, int[][] array) {
 
    M = N;
 
    // Find the maximum length possible
    int minlen = Integer.MAX_VALUE;
    for (int i = 0; i < array.length; i++) {
      minlen = Math.min(minlen, (int) array[i].length);
    }
 
    // Binary search on the length
    int start = 0, end = minlen, mid = 0;
    while (start <= end) {
      mid = (start + end) / 2;
 
      // Function Call to check whether
      // it is possible or not
      if (check(array, mid)) {
        start = mid + 1;
      } else {
        end = mid - 1;
      }
    }
    return end;
  }
 
  // Driver Code
  public static void main(String args[]) {
    int[][] arr = { { 0, 1, 2, 3, 4 }, { 2, 3, 4 }, { 4, 0, 1, 2, 3 } };
 
    long N = arr.length;
 
    System.out.println(longestCommonSubpath(N, arr));
  }
}
 
// This code is contributed by gfgking.


Python3




# Python Program to implement
# the above approach
p = 1299827
mod = 1e11 + 7
M = None
 
# Function to implement rabin - carp
# hashing to check whether the given length
# is possible or not
def check(array, _len, M):
    if (_len == 0):
        return True
    freq = {}
 
    for i in range(M):
        curr_hash = 0
        pow = 1
        found_hashes = set()
        for j in range(_len):
            curr_hash = (curr_hash * p) % mod
            curr_hash = curr_hash + array[i][j]
            if (j != _len - 1):
                pow = (pow * p) % mod
 
        found_hashes.add(curr_hash)
        for j in range(_len, len(array[i])):
            curr_hash = curr_hash + mod
            curr_hash = curr_hash - (array[i][j - _len] * pow) % mod
            curr_hash = curr_hash % mod
            curr_hash = curr_hash * p
            curr_hash = curr_hash % mod
            curr_hash = curr_hash + array[i][j]
            curr_hash = curr_hash % mod
            found_hashes.add(curr_hash)
        while (len(found_hashes) != 0):
            it = list(found_hashes)
 
            # get first entry:
            h = it[0]
            found_hashes.remove(h)
 
            if (h not in freq):
                freq[h] = 1
            else:
                freq[h] += 1
 
            if (h in freq and freq[h] == M):
                return True
    return False
 
# Function to find the longest common sub-array
# from the given N arrays
def longestCommonSubpath(N, array):
    M = N
 
    # Find the maximum length possible
    minlen = 10 ** 9
    for i in range(len(array)):
        minlen = min(minlen, len(array[i]))
 
    # Binary search on the length
    start = 0
    end = minlen
    mid = 0
    while (start <= end):
        mid = (start + end) // 2
 
        # Function Call to check whether
        # it is possible or not
        if (check(array, mid, M)):
            start = mid + 1
        else:
            end = mid - 1
    return end
 
# Driver Code
arr = [[0, 1, 2, 3, 4], [2, 3, 4], [4, 0, 1, 2, 3]]
 
N = len(arr)
print(longestCommonSubpath(N, arr))
 
# This code is contributed by Saurabh Jaiswal


C#




using System;
using System.Collections.Generic;
 
class GFG {
 
    static long p = 1299827;
    static long mod = (long)1E11 + 7;
    static long M;
 
    // Function to implement rabin - carp
    // hashing to check whether the given length
    // is possible or not
    static bool check(int[][] array, int len) {
        if (len == 0)
            return true;
        var freq = new Dictionary<long, int>();
        for (int i = 0; i < M; i++) {
            long curr_hash = 0, pow = 1;
            var found_hashes = new HashSet<long>();
            for (int j = 0; j < len; j++) {
                curr_hash = (curr_hash * p) % mod;
                curr_hash += array[i][j];
                if (j != len - 1)
                    pow = (pow * p) % mod;
            }
            found_hashes.Add(curr_hash);
            for (int j = len; j < array[i].Length; j++) {
                curr_hash += mod;
                curr_hash -= (array[i][j - len] * pow) % mod;
                curr_hash %= mod;
                curr_hash = curr_hash * p;
                curr_hash %= mod;
                curr_hash += array[i][j];
                curr_hash %= mod;
                found_hashes.Add(curr_hash);
            }
            while (found_hashes.Count > 0) {
                long h = found_hashes.GetEnumerator().Current;
                found_hashes.Remove(h);
                if (freq.ContainsKey(h)) {
                    freq[h] += 1;
                }
                else {
                    freq[h] = 1;
                }
                if (freq[h] == M)
                    return true;
            }
        }
        return false;
    }
 
    // Function to find the longest common sub-array
    // from the given N arrays
    public static int longestCommonSubpath(long N, int[][] array) {
 
        M = N;
 
        // Find the maximum length possible
        int minlen = int.MaxValue;
        for (int i = 0; i < array.Length; i++) {
            minlen = Math.Min(minlen, array[i].Length);
        }
 
        // Binary search on the length
        int start = 0, end = minlen, mid = 0;
        while (start <= end) {
            mid = (start + end) / 2;
 
            // Function Call to check whether
            // it is possible or not
            if (check(array, mid)) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }
        return end - 1;
    }
    static void Main(string[] args) {
        int[][] arr = {
            new int[] { 0, 1, 2, 3, 4 },
            new int[] { 2, 3, 4 },
            new int[] { 4, 0, 1, 2, 3 }
        };
 
        long N = arr.Length;
 
        Console.WriteLine(longestCommonSubpath(N, arr));
        Console.ReadKey();
    }
}


Javascript




<script>
      // JavaScript Program to implement
      // the above approach
      let p = 1299827;
      let mod = 1e11 + 7;
      var M;
 
      // Function to implement rabin - carp
      // hashing to check whether the given length
      // is possible or not
      function check(array, len, M) {
          if (len == 0)
              return true;
          let freq = new Map();
 
          for (let i = 0; i < M; i++) {
              let curr_hash = 0, pow = 1;
              let found_hashes = new Set();
              for (let j = 0; j < len; j++) {
                  curr_hash = (curr_hash * p) % mod;
                  curr_hash = curr_hash + array[i][j];
                  if (j != len - 1)
                      pow = (pow * p) % mod;
              }
              found_hashes.add(curr_hash);
              for (let j = len; j < array[i].length; j++) {
                  curr_hash = curr_hash + mod;
                  curr_hash = curr_hash - (array[i][j - len] * pow) % mod;
                  curr_hash = curr_hash % mod;
                  curr_hash = curr_hash * p;
                  curr_hash = curr_hash % mod;
                  curr_hash = curr_hash + array[i][j];
                  curr_hash = curr_hash % mod;
                  found_hashes.add(curr_hash);
              }
 
              while (found_hashes.size != 0)
              {
                  var it = found_hashes.values();
               
                  // get first entry:
                  var first = it.next();
                   
                  // get value out of the iterator entry:
                  var h = first.value;
 
 
                  found_hashes.delete(h);
 
                  if (freq.has(h) == false) {
                      freq.set(h, 1)
                  }
                  else {
                      freq.set(h, freq.get(h) + 1)
                  }
 
                  if (freq.has(h) && freq.get(h) == M)
                      return true;
              }
          }
          return false;
      }
 
      // Function to find the longest common sub-array
      // from the given N arrays
      function longestCommonSubpath(N,
          array) {
 
          M = N;
 
          // Find the maximum length possible
          let minlen = Number.MAX_SAFE_INTEGER;
          for (let i = 0; i < array.length; i++) {
              minlen = Math.min(minlen, array[i].length);
          }
 
          // Binary search on the length
          let start = 0, end = minlen, mid = 0;
          while (start <= end) {
              let mid = Math.floor((start + end) / 2);
 
              // Function Call to check whether
              // it is possible or not
              if (check(array, mid, M)) {
                  start = mid + 1;
              }
              else {
                  end = mid - 1;
              }
          }
          return end;
      }
 
      // Driver Code
      let arr = [[0, 1, 2, 3, 4],
      [2, 3, 4],
      [4, 0, 1, 2, 3]];
 
      let N = arr.length;
 
      document.write(longestCommonSubpath(N, arr));
 
  // This code is contributed by Potta Lokesh
  </script>


Output

2

Time Complexity:  O(N*log(N))
Auxiliary Space: O(N)



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads