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++ 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 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. |
# 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 |
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();
}
} |
<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)