给定两个长度分别为N和M 的数组arr[]和arr1[] ,任务是找到数组arr[]的最长递增子序列,使其不包含数组arr1[]作为子数组。
例子:
Input: arr[] = {5, 3, 9, 3, 4, 7}, arr1[] = {3, 3, 7}
Output: 4
Explanation: Required longest increasing subsequence is {3, 3, 4, 7}.
Input: arr[] = {1, 2, 3}, arr1[] = {1, 2, 3}
Output: 2
Explanation: Required longest increasing subsequence is {1, 2}.
朴素的方法:最简单的方法是生成给定数组的所有可能子序列,并打印其中最长子序列的长度,其中不包含 arr1[] 作为子数组。
时间复杂度: O(M * 2 N ) 其中 N 和 M 是给定数组的长度。
辅助空间: O(M + N)
有效的方法:想法是使用使用 KMP 算法和动态规划生成的 lps[] 数组来查找最长的非递减子序列,没有任何子数组等于sequence[] 。请按照以下步骤解决问题:
- 初始化一个数组dp[N][N][N]其中dp[i][j][K]存储非递减子序列的最大长度直到索引i ,其中j是数组中先前选择的元素的索引arr[]和K表示当前找到的序列包含子数组sequence[0, K] 。
- 此外,使用 KMP 算法生成一个数组来存储最长前缀后缀的长度。
- 可以通过记住以下 dp 转换来找到最大长度:
dp(i, prev, k) = max(1 + dp(i + 1, i, k2), dp(i + 1, prev, k)) where,
- i is the current index.
- prev is the previously chosen element.
- k2 is index of prefix subarray included so far in the currently found sequence which can be found using KMP Array for longest prefix suffix.
Base Case:
- If k is equals to the length of the given sequence, return as the currently found subsequence contains the arr1[].
- If i reaches N, return as no more elements exist.
- If the current state has already been calculated, return.
下面是上述方法的实现:
C++
// C++ program for the above approach
#include
using namespace std;
// Initialize dp and KMP array
int dp[6][6][6];
int KMPArray[2];
// Length of the given sequence[]
int m;
// Function to find the max-length
// subsequence that does not have
// subarray sequence[]
int findSubsequence(int a[], int sequence[], int i,
int prev, int k, int al, int sl)
{
// Stores the subsequence
// explored so far
if (k == m)
return INT_MIN;
// Base Case
if (i == al)
return 0;
// Using memoization to
// avoid re-computation
if (prev != -1 && dp[i][prev][k] != -1) {
return dp[i][prev][k];
}
int include = 0;
if (prev == -1 || a[i] >= a[prev]) {
int k2 = k;
// Using KMP array to find
// corresponding index in arr1[]
while (k2 > 0
&& a[i] != sequence[k2])
k2 = KMPArray[k2 - 1];
// Incrementing k2 as we are
// including this element in
// the subsequence
if (a[i] == sequence[k2])
k2++;
// Possible answer for
// current state
include = 1
+ findSubsequence(
a, sequence,
i + 1, i, k2, al, sl);
}
// Maximum answer for
// current state
int ans = max(
include, findSubsequence(
a, sequence,
i + 1, prev, k, al, sl));
// Memoizing the answer for
// the corresponding state
if (prev != -1) {
dp[i][prev][k] = ans;
}
// Return the answer for
// current state
return ans;
}
// Function that generate KMP Array
void fillKMPArray(int pattern[])
{
// Previous longest prefix suffix
int j = 0;
int i = 1;
// KMPArray[0] is a always 0
KMPArray[0] = 0;
// The loop calculates KMPArray[i]
// for i = 1 to M - 1
while (i < m) {
// If current character is
// same
if (pattern[i] == pattern[j]) {
j++;
// Update the KMP array
KMPArray[i] = j;
i++;
}
// Otherwise
else {
// Update the KMP array
if (j != 0)
j = KMPArray[j - 1];
else {
KMPArray[i] = j;
i++;
}
}
}
}
// Function to print the maximum
// possible length
void printAnswer(int a[], int sequence[], int al, int sl)
{
// Length of the given sequence
m = sl;
// Generate KMP array
fillKMPArray(sequence);
// Initialize the states to -1
memset(dp, -1, sizeof(dp));
// Get answer
int ans = findSubsequence(a, sequence, 0, -1, 0, al, sl);
// Print answer
cout << ((ans < 0) ? 0 : ans) << endl;
}
// Driver code
int main()
{
// Given array
int arr[] = { 5, 3, 9, 3, 4, 7 };
// Give arr1
int arr1[] = { 3, 4 };
// Function Call
printAnswer(arr, arr1, 6, 2);
return 0;
}
// This code is contributed by divyeshrabadiya07.
Java
// Java program for the above approach
import java.io.*;
import java.util.*;
class GFG {
// Initialize dp and KMP array
static int[][][] dp;
static int[] KMPArray;
// Length of the given sequence[]
static int m;
// Function to find the max-length
// subsequence that does not have
// subarray sequence[]
private static int findSubsequence(
int[] a, int[] sequence,
int i, int prev, int k)
{
// Stores the subsequence
// explored so far
if (k == m)
return Integer.MIN_VALUE;
// Base Case
if (i == a.length)
return 0;
// Using memoization to
// avoid re-computation
if (prev != -1
&& dp[i][prev][k] != -1) {
return dp[i][prev][k];
}
int include = 0;
if (prev == -1 || a[i] >= a[prev]) {
int k2 = k;
// Using KMP array to find
// corresponding index in arr1[]
while (k2 > 0
&& a[i] != sequence[k2])
k2 = KMPArray[k2 - 1];
// Incrementing k2 as we are
// including this element in
// the subsequence
if (a[i] == sequence[k2])
k2++;
// Possible answer for
// current state
include = 1
+ findSubsequence(
a, sequence,
i + 1, i, k2);
}
// Maximum answer for
// current state
int ans = Math.max(
include, findSubsequence(
a, sequence,
i + 1, prev, k));
// Memoizing the answer for
// the corresponding state
if (prev != -1) {
dp[i][prev][k] = ans;
}
// Return the answer for
// current state
return ans;
}
// Function that generate KMP Array
private static void
fillKMPArray(int[] pattern)
{
// Previous longest prefix suffix
int j = 0;
int i = 1;
// KMPArray[0] is a always 0
KMPArray[0] = 0;
// The loop calculates KMPArray[i]
// for i = 1 to M - 1
while (i < m) {
// If current character is
// same
if (pattern[i] == pattern[j]) {
j++;
// Update the KMP array
KMPArray[i] = j;
i++;
}
// Otherwise
else {
// Update the KMP array
if (j != 0)
j = KMPArray[j - 1];
else {
KMPArray[i] = j;
i++;
}
}
}
}
// Function to print the maximum
// possible length
static void printAnswer(
int a[], int sequence[])
{
// Length of the given sequence
m = sequence.length;
// Initialize kmp array
KMPArray = new int[m];
// Generate KMP array
fillKMPArray(sequence);
// Initialize dp
dp = new int[a.length][a.length][a.length];
// Initialize the states to -1
for (int i = 0; i < a.length; i++)
for (int j = 0; j < a.length; j++)
Arrays.fill(dp[i][j], -1);
// Get answer
int ans = findSubsequence(
a, sequence, 0, -1, 0);
// Print answer
System.out.println((ans < 0) ? 0 : ans);
}
// Driver code
public static void
main(String[] args) throws Exception
{
// Given array
int[] arr = { 5, 3, 9, 3, 4, 7 };
// Give arr1
int[] arr1 = { 3, 4 };
// Function Call
printAnswer(arr, arr1);
}
}
C#
// C# program for the above approach
using System;
using System.Collections;
using System.Collections.Generic;
class GFG{
// Initialize dp and KMP array
static int[,,] dp;
static int[] KMPArray;
// Length of the given sequence[]
static int m;
// Function to find the max-length
// subsequence that does not have
// subarray sequence[]
private static int findSubsequence(int[] a,
int[] sequence,
int i, int prev,
int k)
{
// Stores the subsequence
// explored so far
if (k == m)
return int.MinValue;
// Base Case
if (i == a.Length)
return 0;
// Using memoization to
// avoid re-computation
if (prev != -1 && dp[i, prev, k] != -1)
{
return dp[i, prev, k];
}
int include = 0;
if (prev == -1 || a[i] >= a[prev])
{
int k2 = k;
// Using KMP array to find
// corresponding index in arr1[]
while (k2 > 0 && a[i] != sequence[k2])
k2 = KMPArray[k2 - 1];
// Incrementing k2 as we are
// including this element in
// the subsequence
if (a[i] == sequence[k2])
k2++;
// Possible answer for
// current state
include = 1 + findSubsequence(a, sequence,
i + 1, i, k2);
}
// Maximum answer for
// current state
int ans = Math.Max(include,
findSubsequence(a, sequence,
i + 1, prev, k));
// Memoizing the answer for
// the corresponding state
if (prev != -1)
{
dp[i, prev, k] = ans;
}
// Return the answer for
// current state
return ans;
}
// Function that generate KMP Array
private static void fillKMPArray(int[] pattern)
{
// Previous longest prefix suffix
int j = 0;
int i = 1;
// KMPArray[0] is a always 0
KMPArray[0] = 0;
// The loop calculates KMPArray[i]
// for i = 1 to M - 1
while (i < m)
{
// If current character is
// same
if (pattern[i] == pattern[j])
{
j++;
// Update the KMP array
KMPArray[i] = j;
i++;
}
// Otherwise
else
{
// Update the KMP array
if (j != 0)
j = KMPArray[j - 1];
else
{
KMPArray[i] = j;
i++;
}
}
}
}
// Function to print the maximum
// possible length
static void printAnswer(int[] a, int[] sequence)
{
// Length of the given sequence
m = sequence.Length;
// Initialize kmp array
KMPArray = new int[m];
// Generate KMP array
fillKMPArray(sequence);
// Initialize dp
dp = new int[a.Length, a.Length, a.Length];
// Initialize the states to -1
for(int i = 0; i < a.Length; i++)
for(int j = 0; j < a.Length; j++)
for(int k = 0; k < a.Length; k++)
dp[i, j, k] = -1;
// Get answer
int ans = findSubsequence(a, sequence, 0, -1, 0);
// Print answer
Console.WriteLine((ans < 0) ? 0 : ans);
}
// Driver code
public static void Main()
{
// Given array
int[] arr = { 5, 3, 9, 3, 4, 7 };
// Give arr1
int[] arr1 = { 3, 4 };
// Function Call
printAnswer(arr, arr1);
}
}
// This code is contributed by akhilsaini
3
时间复杂度: O(N 3 )
辅助空间: O(N 3 )
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。