Length of longest common subarray for given N arrays
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> |
2
Time Complexity: O(N*log(N))
Auxiliary Space: O(N)
Please Login to comment...