Maximum length subsequence such that adjacent elements in the subsequence have a common factor

Given an array arr[], the task is to find the maximum length of a subsequence such that the adjacent elements in the subsequence have a common factor. 

Examples: 

Input: arr[] = { 13, 2, 8, 6, 3, 1, 9 } 
Output: 5
Max length subsequence with satisfied conditions: { 2, 8, 6, 3, 9 } 

Input: arr[] = { 12, 2, 8, 6, 3, 1, 9 } 
Output: 6
Max length subsequence with satisfied conditions: {12, 2, 8, 6, 3, 9 }

Input: arr[] = { 1, 2, 2, 3, 3, 1 } 
Output:



Approach: A naive approach is to consider all subsequences and check every subsequence whether it satisfies the condition. 
An efficient solution is to use Dynamic programming. Let dp[i] denote the maximum length of subsequence including arr[i]. Then, the following relation holds for every prime p such that p is a prime factor of arr[i]:

dp[i] = max(dp[i], 1 + dp[pos[p]]) 
where pos[p] gives the index of p in the array 
where it last occurred.

Explanation: Traverse the array. For an element arr[i], there are 2 possibilities. 

  1. If the prime factors of arr[i] have shown their first appearance in the array, then dp[i] = 1
  2. If the prime factors of arr[i] have already occurred, then this element can be added in the subsequence since there’s a common factor. Hence dp[i] = max(dp[i], 1 + dp[pos[p]]) where p is the common prime factor and pos[p] is the latest index of p in the array.

Below is the implementation of the above approach: 

filter_none

edit
close

play_arrow

link
brightness_4
code

// C++ implementation of the above approach
#include <bits/stdc++.h>
#define N 100005
#define MAX 10000002
 
using namespace std;
 
int lpd[MAX];
 
// Function to compute least
// prime divisor of i
void preCompute()
{
    memset(lpd, 0, sizeof(lpd));
    lpd[0] = lpd[1] = 1;
    for (int i = 2; i * i < MAX; i++)
    {
        for (int j = i * 2; j < MAX; j += i)
        {
            if (lpd[j] == 0)
            {
                lpd[j] = i;
            }
        }
    }
    for (int i = 2; i < MAX; i++)
    {
        if (lpd[i] == 0)
        {
            lpd[i] = i;
        }
    }
}
 
// Function that returns the maximum
// length subsequence such that
// adjacent elements have a common factor.
int maxLengthSubsequence(int arr[], int n)
{
    int dp[N];
    unordered_map<int, int> pos;
 
    // Initialize dp array with 1.
    for (int i = 0; i <= n; i++)
        dp[i] = 1;
 
    for (int i = 0; i <= n; i++)
    {
        while (arr[i] > 1)
        {
            int p = lpd[arr[i]];
            if (pos[p])
            {
                // p has appeared at least once.
                dp[i] = max(dp[i], 1 + dp[pos[p]]);
            }
 
            // Update latest occurrence of prime p.
            pos[p] = i;
            while (arr[i] % p == 0)
                arr[i] /= p;
        }
    }
 
    // Take maximum value as the answer.
    int ans = 1;
    for (int i = 0; i <= n; i++)
    {
        ans = max(ans, dp[i]);
    }
 
    return ans;
}
 
// Driver code
int main()
{
    int arr[] = { 13, 2, 8, 6, 3, 1, 9 };
    int n = sizeof(arr) / sizeof(arr[0]);
 
    preCompute();
 
    cout << maxLengthSubsequence(arr, n);
    return 0;
}
chevron_right

filter_none

edit
close

play_arrow

link
brightness_4
code

# Python3 implementation of the
# above approach
import math as mt
 
N = 100005
MAX = 1000002
 
lpd = [0 for i in range(MAX)]
 
# to compute least prime divisor of i
 
 
def preCompute():
 
    lpd[0], lpd[1] = 1, 1
 
    for i in range(2, mt.ceil(mt.sqrt(MAX))):
        for j in range(2 * i, MAX, i):
            if (lpd[j] == 0):
                lpd[j] = i
 
    for i in range(2, MAX):
        if (lpd[i] == 0):
            lpd[i] = i
 
# Function that returns the maximum
# length subsequence such that
# adjacent elements have a common factor.
 
 
def maxLengthSubsequence(arr, n):
    dp = [1 for i in range(N + 1)]
 
    pos = dict()
 
    # Initialize dp array with 1.
    for i in range(0, n):
        while (arr[i] > 1):
            p = lpd[arr[i]]
            if (p in pos.keys()):
 
                # p has appeared at least once.
                dp[i] = max(dp[i], 1 + dp[pos[p]])
 
            # Update latest occurrence of prime p.
            pos[p] = i
            while (arr[i] % p == 0):
                arr[i] //= p
 
    # Take maximum value as the answer.
    ans = 1
    for i in range(0, n + 1):
        ans = max(ans, dp[i])
 
    return ans
 
 
# Driver code
arr = [13, 2, 8, 6, 3, 1, 9]
n = len(arr)
 
preCompute()
 
print(maxLengthSubsequence(arr, n))
 
# This code is contributed by Mohit Kumar
chevron_right

filter_none

edit
close

play_arrow

link
brightness_4
code

// Java implementation of the above approach
import java.util.*;
class GfG {
    static int N, MAX;
 
    // Check if UpperBound is
    // given for all test Cases
    // N = 100005 ;
    // MAX = 10000002;
    static int lpd[];
 
    // Function to compute least prime divisor
    // of i upto MAX element of the  input array
    // it will be space efficient
    // if more test cases are there it's
    // better to find prime divisor
    // upto upperbound of input element
    // it will be cost efficient
    static void preCompute()
    {
        lpd = new int[MAX + 1];
        lpd[0] = lpd[1] = 1;
        for (int i = 2; i * i <= MAX; i++)
        {
            for (int j = i * 2; j <= MAX; j += i)
            {
                if (lpd[j] == 0)
                {
                    lpd[j] = i;
                }
            }
        }
        for (int i = 2; i <= MAX; i++)
        {
            if (lpd[i] == 0)
            {
                lpd[i] = i;
            }
        }
    }
 
    // Function that returns the maximum
    // length subsequence such that
    // adjacent elements have a common factor.
    static int maxLengthSubsequence(Integer arr[], int n)
    {
        Integer dp[] = new Integer[N];
        Map<Integer, Integer> pos
            = new HashMap<Integer, Integer>();
 
        // Initialize dp array with 1.
        for (int i = 0; i <= n; i++)
            dp[i] = 1;
 
        for (int i = 0; i <= n; i++)
        {
            while (arr[i] > 1) {
                int p = lpd[arr[i]];
                if (pos.containsKey(p))
                {
                    // p has appeared at least once.
                    dp[i] = Math.max(dp[i],
                                     1 + dp[pos.get(p)]);
                }
 
                // Update latest occurrence of prime p.
                pos.put(p, i);
                while (arr[i] % p == 0)
                    arr[i] /= p;
            }
        }
 
        // Take maximum value as the answer.
        int ans = Collections.max(Arrays.asList(dp));
        return ans;
    }
 
    // Driver code
    public static void main(String[] args)
    {
        Integer arr[] = { 12, 2, 8, 6, 3, 1, 9 };
        N = arr.length;
        MAX = Collections.max(Arrays.asList(arr));
        preCompute();
        System.out.println(
            maxLengthSubsequence(arr, N - 1));
    }
}
 
// This code is contributed by Prerna Saini.
chevron_right

filter_none

edit
close

play_arrow

link
brightness_4
code

// C# implementation of the
// above approach
using System;
using System.Collections;
 
class GFG {
 
    static int N = 100005;
    static int MAX = 10000002;
 
    static int[] lpd = new int[MAX];
 
    // to compute least prime divisor of i
    static void preCompute()
    {
        lpd[0] = lpd[1] = 1;
        for (int i = 2; i * i < MAX; i++)
        {
            for (int j = i * 2; j < MAX; j += i)
            {
                if (lpd[j] == 0)
                {
                    lpd[j] = i;
                }
            }
        }
        for (int i = 2; i < MAX; i++)
        {
            if (lpd[i] == 0)
            {
                lpd[i] = i;
            }
        }
    }
 
    // Function that returns the maximum
    // length subsequence such that
    // adjacent elements have a common factor.
    static int maxLengthSubsequence(int[] arr, int n)
    {
        int[] dp = new int[N];
        Hashtable pos = new Hashtable();
 
        // Initialize dp array with 1.
        for (int i = 0; i <= n; i++)
            dp[i] = 1;
 
        for (int i = 0; i <= n; i++)
        {
            while (arr[i] > 1) {
                int p = lpd[arr[i]];
                if (pos.ContainsKey(p))
                {
                    // p has appeared at least once.
                    dp[i] = Math.Max(
                        dp[i],
                        1 + dp[Convert.ToInt32(pos[p])]);
                }
 
                // Update latest occurrence of prime p.
                pos[p] = i;
                while (arr[i] % p == 0)
                    arr[i] /= p;
            }
        }
 
        // Take maximum value as the answer.
        int ans = 1;
        for (int i = 0; i <= n; i++)
        {
            ans = Math.Max(ans, dp[i]);
        }
 
        return ans;
    }
 
    // Driver code
    public static void Main()
    {
        int[] arr = { 13, 2, 8, 6, 3, 1, 9 };
        int n = arr.Length - 1;
 
        preCompute();
        Console.WriteLine(maxLengthSubsequence(arr, n));
    }
}
 
// This code is contributed by Ryuga
chevron_right

Output
5

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




Recommended Posts:


An enthusiastic Java and web developer with a little affinity for tea, cricket, English, etymology, and reading

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.



Article Tags :