📜  给定GCD的最小子阵列

📅  最后修改于: 2021-04-17 13:19:05             🧑  作者: Mango

给定一个n个数字的数组arr []和一个整数k,找到gcd等于k的最小子数组的长度。

例子:

Input: arr[] = {6, 9, 7, 10, 12, 
                24, 36, 27}, 
           K = 3
Output: 2
Explanation: GCD of subarray {6,9} is 3.
GCD of subarray {24,36,27} is also 3,
but {6,9} is the smallest 

注意:以下方法的时间复杂度分析假设数字是固定大小的,并且找到两个元素的GCD需要恒定的时间。

方法1

查找所有子阵列的GCD,并使用gcd k跟踪最小长度的子阵列。其时间复杂度为O(n 3 ),每个子阵列为O(n 2 ),O(n)用于找到子阵列的gcd。

方法2

使用此处讨论的基于分段树的方法查找所有子阵列的GCD。该解决方案的时间复杂度为O(n 2 logn),每个子阵列为O(n 2 ),以及使用段树查找子阵列的GCD的O(logn)。

方法3

这个想法是使用段树和二进制搜索来实现时间复杂度O(n(logn) 2 )。

  1. 如果我们在数组中有任何等于“ k”的数字,则答案为1,因为k的GCD为k。返回1。
  2. 如果没有可被k整除的数字,则GCD不存在。返回-1。
  3. 如果以上情况均不成立,则最小子数组的长度大于1或GCD不存在。在这种情况下,我们将按照以下步骤操作。
    • 构建段树,以便我们可以使用此处讨论的方法快速找到任何子数组的GCD
    • 建立区隔树后,我们将每个索引视为起点,并对终点进行二进制搜索,以使这两点之间的子数组具有GCD k

以下是上述想法的实现。

C++
// C++ Program to find GCD of a number in a given Range
// using segment Trees
#include 
using namespace std;
  
// To store segment tree
int *st;
  
// Function to find gcd of 2 numbers.
int gcd(int a, int b)
{
    if (a < b)
        swap(a, b);
    if (b==0)
        return a;
    return gcd(b, a%b);
}
  
/*  A recursive function to get gcd of given
    range of array indexes. The following are parameters for
    this function.
  
    st    --> Pointer to segment tree
    si --> Index of current node in the segment tree. Initially
               0 is passed as root is always at index 0
    ss & se  --> Starting and ending indexes of the segment
                 represented by current node, i.e., st[index]
    qs & qe  --> Starting and ending indexes of query range */
int findGcd(int ss, int se, int qs, int qe, int si)
{
    if (ss>qe || se < qs)
        return 0;
    if (qs<=ss && qe>=se)
        return st[si];
    int mid = ss+(se-ss)/2;
    return gcd(findGcd(ss, mid, qs, qe, si*2+1),
               findGcd(mid+1, se, qs, qe, si*2+2));
}
  
//Finding The gcd of given Range
int findRangeGcd(int ss, int se, int arr[], int n)
{
    if (ss<0 || se > n-1 || ss>se)
    {
        cout << "Invalid Arguments" << "\n";
        return -1;
    }
    return findGcd(0, n-1, ss, se, 0);
}
  
// A recursive function that constructs Segment Tree for
// array[ss..se]. si is index of current node in segment
// tree st
int constructST(int arr[], int ss, int se, int si)
{
    if (ss==se)
    {
        st[si] = arr[ss];
        return st[si];
    }
    int mid = ss+(se-ss)/2;
    st[si] = gcd(constructST(arr, ss, mid, si*2+1),
                 constructST(arr, mid+1, se, si*2+2));
    return st[si];
}
  
/* Function to construct segment tree from given array.
   This function allocates memory for segment tree and
   calls constructSTUtil() to fill the allocated memory */
int *constructSegmentTree(int arr[], int n)
{
    int height = (int)(ceil(log2(n)));
    int size = 2*(int)pow(2, height)-1;
    st = new int[size];
    constructST(arr, 0, n-1, 0);
    return st;
}
  
// Returns size of smallest subarray of arr[0..n-1]
// with GCD equal to k.
int findSmallestSubarr(int arr[], int n, int k)
{
    // To check if a multiple of k exists.
    bool found = false;
  
    // Find if k or its multiple is present
    for (int i=0; i k)
                low = mid;
  
            // If GCD is k, store this point and look for
            // a closer point
            else if (gcd == k)
            {
                high = mid;
                closest = mid;
                break;
            }
  
            // If GCD is less than, look closer
            else
                high = mid;
  
            // If termination condition reached, set
            // closest
            if (abs(high-low) <= 1)
            {
                if (findRangeGcd(i, low, arr, n) == k)
                    closest = low;
                else if (findRangeGcd(i, high, arr, n)==k)
                    closest = high;
                break;
            }
        }
  
        if (closest != 0)
            res = min(res, closest - i + 1);
    }
  
    // If res was not changed by loop, return -1,
    // else return its value.
    return (res == n+1) ? -1 : res;
}
  
// Driver program to test above functions
int main()
{
    int n = 8;
    int k = 3;
    int arr[] = {6, 9, 7, 10, 12, 24, 36, 27};
    cout << "Size of smallest sub-array with given"
         << " size is " << findSmallestSubarr(arr, n, k);
    return 0;
}


Java
// Java Program to find GCD of a number in a given Range
// using segment Trees
class GFG 
{
  
// To store segment tree
static int []st;
  
// Function to find gcd of 2 numbers.
static int gcd(int a, int b)
{
    if (a < b)
        swap(a, b);
    if (b == 0)
        return a;
    return gcd(b, a % b);
}
  
private static void swap(int x, int y) 
{
    int temp = x;
    x = y;
    y = temp;
} 
/* A recursive function to get gcd of given
    range of array indexes. The following are parameters for
    this function.
  
    st --> Pointer to segment tree
    si --> Index of current node in the segment tree. Initially
            0 is passed as root is always at index 0
    ss & se --> Starting and ending indexes of the segment
                represented by current node, i.e., st[index]
    qs & qe --> Starting and ending indexes of query range */
static int findGcd(int ss, int se, int qs, int qe, int si)
{
    if (ss > qe || se < qs)
        return 0;
    if (qs <= ss && qe >= se)
        return st[si];
    int mid = ss + (se - ss) / 2;
    return gcd(findGcd(ss, mid, qs, qe, si * 2 + 1),
            findGcd(mid + 1, se, qs, qe, si * 2 + 2));
}
  
// Finding The gcd of given Range
static int findRangeGcd(int ss, int se, int arr[], int n)
{
    if (ss < 0 || se > n-1 || ss > se)
    {
        System.out.println("Invalid Arguments");
        return -1;
    }
    return findGcd(0, n - 1, ss, se, 0);
}
  
// A recursive function that constructs Segment Tree for
// array[ss..se]. si is index of current node in segment
// tree st
static int constructST(int arr[], int ss, int se, int si)
{
    if (ss == se)
    {
        st[si] = arr[ss];
        return st[si];
    }
    int mid = ss + (se - ss) / 2;
    st[si] = gcd(constructST(arr, ss, mid, si * 2 + 1),
                constructST(arr, mid+1, se, si * 2 + 2));
    return st[si];
}
  
/* Function to construct segment tree from given array.
This function allocates memory for segment tree and
calls constructSTUtil() to fill the allocated memory */
static int []constructSegmentTree(int arr[], int n)
{
    int height = (int)(Math.ceil(Math.log(n)));
    int size = 2*(int)Math.pow(2, height) - 1;
    st = new int[size];
    constructST(arr, 0, n - 1, 0);
    return st;
}
  
// Returns size of smallest subarray of arr[0..n-1]
// with GCD equal to k.
static int findSmallestSubarr(int arr[], int n, int k)
{
    // To check if a multiple of k exists.
    boolean found = false;
  
    // Find if k or its multiple is present
    for (int i = 0; i < n; i++)
    {
        // If k is present, then subarray size is 1.
        if (arr[i] == k)
            return 1;
  
        // Break the loop to indicate presence of a
        // multiple of k.
        if (arr[i] % k == 0)
            found = true;
    }
  
    // If there was no multiple of k in arr[], then
    // we can't get k as GCD.
    if (found == false)
        return -1;
  
    // If there is a multiple of k in arr[], build
    // segment tree from given array
    constructSegmentTree(arr, n);
  
    // Initialize result
    int res = n + 1;
  
    // Now consider every element as starting point
    // and search for ending point using Binary Search
    for (int i = 0; i < n - 1; i++)
    {
        // a[i] cannot be a starting point, if it is
        // not a multiple of k.
        if (arr[i] % k != 0)
            continue;
  
        // Initialize indexes for binary search of closest
        // ending point to i with GCD of subarray as k.
        int low = i + 1;
        int high = n - 1;
  
        // Initialize closest ending point for i.
        int closest = 0;
  
        // Binary Search for closest ending point
        // with GCD equal to k.
        while (true)
        {
            // Find middle point and GCD of subarray
            // arr[i..mid]
            int mid = low + (high-low)/2;
            int gcd = findRangeGcd(i, mid, arr, n);
  
            // If GCD is more than k, look further
            if (gcd > k)
                low = mid;
  
            // If GCD is k, store this point and look for
            // a closer point
            else if (gcd == k)
            {
                high = mid;
                closest = mid;
                break;
            }
  
            // If GCD is less than, look closer
            else
                high = mid;
  
            // If termination condition reached, set
            // closest
            if (Math.abs(high-low) <= 1)
            {
                if (findRangeGcd(i, low, arr, n) == k)
                    closest = low;
                else if (findRangeGcd(i, high, arr, n)==k)
                    closest = high;
                break;
            }
        }
  
        if (closest != 0)
            res = Math.min(res, closest - i + 1);
    }
  
    // If res was not changed by loop, return -1,
    // else return its value.
    return (res == n+1) ? -1 : res;
}
  
// Driver code
public static void main(String args[]) 
{
    int n = 8;
    int k = 3;
    int arr[] = {6, 9, 7, 10, 12, 24, 36, 27};
    System.out.println("Size of smallest sub-array with given"
        + " size is " + findSmallestSubarr(arr, n, k));
}
}
  
// This code is contributed by Rajput-Ji


Python3
# Python Program to find GCD of a number in a given Range
# using segment Trees
  
# To store segment tree
st = []
  
# Function to find gcd of 2 numbers.
def gcd(a: int, b: int) -> int:
    if a < b:
        a, b = b, a
    if b == 0:
        return a
    return gcd(b, a % b)
  
# A recursive function to get gcd of given
# range of array indexes. The following are parameters for
# this function.
  
# st --> Pointer to segment tree
# si --> Index of current node in the segment tree. Initially
#             0 is passed as root is always at index 0
# ss & se --> Starting and ending indexes of the segment
#                 represented by current node, i.e., st[index]
# qs & qe --> Starting and ending indexes of query range
def findGcd(ss: int, se: int, qs: int, qe: int, si: int) -> int:
    if ss > qe or se < qs:
        return 0
    if qs <= ss and qe >= se:
        return st[si]
    mid = ss + (se - ss) // 2
    return gcd(findGcd(ss, mid, qs, qe, si * 2 + 1),
            findGcd(mid + 1, se, qs, qe, si * 2 + 2))
  
# Finding The gcd of given Range
def findRangeGcd(ss: int, se: int, arr: list, n: int) -> int:
    if ss < 0 or se > n - 1 or ss > se:
        print("invalid Arguments")
        return -1
    return findGcd(0, n - 1, ss, se, 0)
  
# A recursive function that constructs Segment Tree for
# array[ss..se]. si is index of current node in segment
# tree st
def constructST(arr: list, ss: int, se: int, si: int) -> int:
    if ss == se:
        st[si] = arr[ss]
        return st[si]
    mid = ss + (se - ss) // 2
    st[si] = gcd(constructST(arr, ss, mid, si * 2 + 1),
                constructST(arr, mid + 1, se, si * 2 + 2))
    return st[si]
  
# Function to construct segment tree from given array.
# This function allocates memory for segment tree and
# calls constructSTUtil() to fill the allocated memory
def constructSegmentTree(arr: list, n: int) -> list:
    global st
    from math import ceil, log2, pow
  
    height = int(ceil(log2(n)))
    size = 2 * int(pow(2, height)) - 1
    st = [0] * size
    constructST(arr, 0, n - 1, 0)
    return st
  
# Returns size of smallest subarray of arr[0..n-1]
# with GCD equal to k.
def findSmallestSubarr(arr: list, n: int, k: int) -> int:
  
    # To check if a multiple of k exists.
    found = False
  
    # Find if k or its multiple is present
    for i in range(n):
  
        # If k is present, then subarray size is 1.
        if arr[i] == k:
            return 1
  
        # Break the loop to indicate presence of a
        # multiple of k.
        if arr[i] % k == 0:
            found = True
  
    # If there was no multiple of k in arr[], then
    # we can't get k as GCD.
    if found == False:
        return -1
  
    # If there is a multiple of k in arr[], build
    # segment tree from given array
    constructSegmentTree(arr, n)
  
    # Initialize result
    res = n + 1
  
    # Now consider every element as starting point
    # and search for ending point using Binary Search
    for i in range(n - 1):
  
        # a[i] cannot be a starting point, if it is
        # not a multiple of k.
        if arr[i] % k != 0:
            continue
  
        # Initialize indexes for binary search of closest
        # ending point to i with GCD of subarray as k.
        low = i + 1
        high = n - 1
  
        # Initialize closest ending point for i.
        closest = 0
  
        # Binary Search for closest ending point
        # with GCD equal to k.
        while True:
  
            # Find middle point and GCD of subarray
            # arr[i..mid]
            mid = low + (high - low) // 2
            gcd = findRangeGcd(i, mid, arr, n)
  
            # If GCD is more than k, look further
            if gcd > k:
                low = mid
  
            # If GCD is k, store this point and look for
            # a closer point
            elif gcd == k:
                high = mid
                closest = mid
                break
  
            # If GCD is less than, look closer
            else:
                high = mid
  
            # If termination condition reached, set
            # closest
            if abs(high - low) <= 1:
                if findRangeGcd(i, low, arr, n) == k:
                    closest = low
                elif findRangeGcd(i, high, arr, n) == k:
                    closest = high
                break
        if closest != 0:
            res = min(res, closest - i + 1)
  
    # If res was not changed by loop, return -1,
    # else return its value.
    return -1 if res == n + 1 else res
  
# Driver Code
if __name__ == "__main__":
    n = 8
    k = 3
    arr = [6, 9, 7, 10, 12, 24, 36, 27]
    print("Size of smallest sub-array with given size is",
        findSmallestSubarr(arr, n, k))
  
# This code is contributed by
# sanjeev2552


C#
// C# Program to find GCD of a number in a given Range
// using segment Trees
using System;
  
class GFG 
{
  
// To store segment tree
static int []st;
  
// Function to find gcd of 2 numbers.
static int gcd(int a, int b)
{
    if (a < b)
        swap(a, b);
    if (b == 0)
        return a;
    return gcd(b, a % b);
}
  
private static void swap(int x, int y) 
{
    int temp = x;
    x = y;
    y = temp;
}
  
/* A recursive function to get gcd of given
    range of array indexes. The following are parameters for
    this function.
  
    st --> Pointer to segment tree
    si --> Index of current node in the segment tree. Initially
            0 is passed as root is always at index 0
    ss & se --> Starting and ending indexes of the segment
                represented by current node, i.e., st[index]
    qs & qe --> Starting and ending indexes of query range */
static int findGcd(int ss, int se, int qs, int qe, int si)
{
    if (ss > qe || se < qs)
        return 0;
    if (qs <= ss && qe >= se)
        return st[si];
    int mid = ss + (se - ss) / 2;
    return gcd(findGcd(ss, mid, qs, qe, si * 2 + 1),
            findGcd(mid + 1, se, qs, qe, si * 2 + 2));
}
  
// Finding The gcd of given Range
static int findRangeGcd(int ss, int se, int []arr, int n)
{
    if (ss < 0 || se > n-1 || ss > se)
    {
        Console.WriteLine("Invalid Arguments");
        return -1;
    }
    return findGcd(0, n - 1, ss, se, 0);
}
  
// A recursive function that constructs Segment Tree for
// array[ss..se]. si is index of current node in segment
// tree st
static int constructST(int []arr, int ss, int se, int si)
{
    if (ss == se)
    {
        st[si] = arr[ss];
        return st[si];
    }
    int mid = ss + (se - ss) / 2;
    st[si] = gcd(constructST(arr, ss, mid, si * 2 + 1),
                constructST(arr, mid+1, se, si * 2 + 2));
    return st[si];
}
  
/* Function to construct segment tree from given array.
This function allocates memory for segment tree and
calls constructSTUtil() to fill the allocated memory */
static int []constructSegmentTree(int []arr, int n)
{
    int height = (int)(Math.Ceiling(Math.Log(n)));
    int size = 2*(int)Math.Pow(2, height) - 1;
    st = new int[size];
    constructST(arr, 0, n - 1, 0);
    return st;
}
  
// Returns size of smallest subarray of arr[0..n-1]
// with GCD equal to k.
static int findSmallestSubarr(int []arr, int n, int k)
{
    // To check if a multiple of k exists.
    bool found = false;
  
    // Find if k or its multiple is present
    for (int i = 0; i < n; i++)
    {
        // If k is present, then subarray size is 1.
        if (arr[i] == k)
            return 1;
  
        // Break the loop to indicate presence of a
        // multiple of k.
        if (arr[i] % k == 0)
            found = true;
    }
  
    // If there was no multiple of k in arr[], then
    // we can't get k as GCD.
    if (found == false)
        return -1;
  
    // If there is a multiple of k in arr[], build
    // segment tree from given array
    constructSegmentTree(arr, n);
  
    // Initialize result
    int res = n + 1;
  
    // Now consider every element as starting point
    // and search for ending point using Binary Search
    for (int i = 0; i < n - 1; i++)
    {
        // a[i] cannot be a starting point, if it is
        // not a multiple of k.
        if (arr[i] % k != 0)
            continue;
  
        // Initialize indexes for binary search of closest
        // ending point to i with GCD of subarray as k.
        int low = i + 1;
        int high = n - 1;
  
        // Initialize closest ending point for i.
        int closest = 0;
  
        // Binary Search for closest ending point
        // with GCD equal to k.
        while (true)
        {
            // Find middle point and GCD of subarray
            // arr[i..mid]
            int mid = low + (high-low)/2;
            int gcd = findRangeGcd(i, mid, arr, n);
  
            // If GCD is more than k, look further
            if (gcd > k)
                low = mid;
  
            // If GCD is k, store this point and look for
            // a closer point
            else if (gcd == k)
            {
                high = mid;
                closest = mid;
                break;
            }
  
            // If GCD is less than, look closer
            else
                high = mid;
  
            // If termination condition reached, set
            // closest
            if (Math.Abs(high-low) <= 1)
            {
                if (findRangeGcd(i, low, arr, n) == k)
                    closest = low;
                else if (findRangeGcd(i, high, arr, n)==k)
                    closest = high;
                break;
            }
        }
  
        if (closest != 0)
            res = Math.Min(res, closest - i + 1);
    }
  
    // If res was not changed by loop, return -1,
    // else return its value.
    return (res == n+1) ? -1 : res;
}
  
// Driver code
public static void Main(String []args) 
{
    int n = 8;
    int k = 3;
    int []arr = {6, 9, 7, 10, 12, 24, 36, 27};
    Console.WriteLine("Size of smallest sub-array with given"
        + " size is " + findSmallestSubarr(arr, n, k));
}
}
  
/* This code is contributed by PrinciRaj1992 */


输出:

Size of smallest sub-array with given size is 2

例子:

arr[] = {6, 9, 7, 10, 12, 24, 36, 27}, K = 3

// Initial value of minLen is equal to size 
// of array
minLen = 8 

No element is equal to k so result is either 
greater than 1 or doesn't exist. 

第一指标

  • 从1到5的子阵列的GCD为1。
  • GCD
  • 从1到3的子数组的GCD为1。
  • GCD
  • 从1到2的子数组的GCD为3
  • minLen =最小值(8,2)= 2

第二索引

  • 子数组从2到5的GCD为1
  • GCD
  • 子数组从2到4的GCD为1
  • GCD
  • 从6到8的子数组的GCD是3
  • minLen =最小值(2,3)= 2。

第六指数

  • 从6到7的子数组的GCD为12
  • GCD> k
  • 从6到8的子数组的GCD是3
  • minLen =最小值(2,3)= 2

时间复杂度: O(n(logn) 2 ),O(n)用于遍历每个索引,O(logn)用于每个子数组,O(logn)用于每个子数组的GCD。