Open In App

Remove consecutive repeated numbers

Last Updated : 26 Dec, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Given an array numbers[] of several numbers that may repeat. You are allowed to remove similar numbers in a range, the score you get after removing these numbers is let k be the number removed from the range then your score increases by k*k. You need to return the maximum score you can get after all the numbers are removed. Consider the examples for more clarity.

Examples:

Input: numbers = {1, 3, 2, 3, 2, 1, 1, 1}
Output: 22
Explanation: The order in which we will follow the removals is as follows, first, we remove 2 at index (2) which adds to 1*1 because we removed just one integer, then the new array is {1, 3, 3, 2, 1, 1, 1}, now we will again remove 2 at index (3) this again adds to 1*1, the new array is {1, 3, 3, 1, 1, 1}, we now remove the range (1-2) the two 3’s, that adds up to 2*2 because we removed 2 numbers, now our array becomes {1,1,1,1} and now we remove the range (0,3) which adds up 4*4 in our sum and now our array is empty. The total sum comprises ( 1*1 + 1*1 + 2*2 + 4*4 ) = 22.

Input: numbers = {1, 1, 1, 2}
Output: 10
Explanation: We at first removed 2 which is at the last index which adds up to 1*1, then our array is {1,1,1} and now we remove the entire range which adds up to 3*3 to our answer. now our answer consist of (1*1 + 3*3 ) = 10.

Approach: To solve the problem follow the below steps:

  1. Function Signature:
    1. int removeNumbers(vector<int>& numbers): The function takes a vector of integers numbers as input and returns an integer.
  2. Memoization:
    1. The code uses an unordered map unordered_map<int,int> dp for memoization. It stores the result of previously computed states to avoid redundant computations.
  3. Recursive Solver Function:
    1. int solver(vector<int>& numbers, int l, int r, int k): This function recursively calculates the maximum sum of squares for a given range [l, r] and a parameter k.
  4. Parameters:
    1. numbers: The input vector of numbers.
    2. l: The left index of the current range.
    3. r: The right index of the current range.
    4. k: A parameter used to track consecutive repeated numbers.
  5. Base Case:
    1. If l > r, the range is empty, so the function returns 0.
  6. Memoization Key:
    1. The memoization key is computed as (l * numbers.size() + r) * numbers.size() + k. It’s designed to uniquely identify each subproblem based on its parameters.
  7. Memoization Check:
    1. Before performing any computations, the function checks if the result for the current state is already computed and stored in dp. If so, it returns the stored result.
  8. Consecutive Repeated Numbers:
    1. The function checks for consecutive repeated numbers at both ends of the current range [l, r]. It updates k accordingly.
  9. Recursion for Subproblems:
    1. The function recursively explores two cases:
      1. Removing the rightmost element (solver(numbers, l, r-1, 0)) and adding k+1 squared to the result.
      2. Splitting the range at position i and recursively solving for both subranges (solver(numbers, l, i, k+1) + solver(numbers, i+1, r-1, 0)).
  10. Memoization Update:
    1. The function updates dp[key] with the maximum result obtained from the above two cases.
  11. Return Value:
    1. The function returns dp[key], which represents the maximum sum of squares for the given range [l, r] and parameter k.
  12. Main Function:
    1. In the main() function, a sample vector numbers is provided, and the removeNumbers function is called to solve the problem.

Below is the implementation of above approach:

C++14




// Include necessary header files
#include <bits/stdc++.h>
using namespace std;
 
unordered_map<int, int> dp;
 
// Recursive function to solve the problem
int solver(vector<int>& numbers, int l, int r, int k)
{
    // Base case: if left index exceeds
    // right index, return 0
    if (l > r)
        return 0;
 
    // Compute a unique key for memoization
    int key = (l * numbers.size() + r) * numbers.size() + k;
 
    // Check if result for current state
    // is already computed
    if (dp.find(key) != dp.end())
        return dp[key];
 
    // Handle consecutive repeated numbers
    // at the right end of the range
    while (r > l and numbers[r] == numbers[r - 1]) {
        r--;
        k++;
    }
 
    // Handle consecutive repeated numbers
    // at the left end of the range
    while (r > l and numbers[l] == numbers[r]) {
        l++;
        k++;
    }
 
    // Update key after handling
    // consecutive repeats
    key = (l * numbers.size() + r) * numbers.size() + k;
 
    // Calculate result by recursively
    // solving subproblems
    dp[key]
        = solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1);
 
    for (int i = l; i < r; i++) {
        if (numbers[i] == numbers[r]) {
            while (i + 1 < r
                   and numbers[i + 1] == numbers[r])
                i++;
            // After taking similar characters
            // from left let the left range be
            // partof the right range and hence
            // add K+1 to previously going on
            // range, and for the range that is
            // left start from k=0 and try
            // removing new sub ranges of similar
            // numbers from this range.
            dp[key]
                = max(dp[key], solver(numbers, l, i, k + 1)
                                   + solver(numbers, i + 1,
                                            r - 1, 0));
        }
    }
 
    // Return the computed result
    // for this state
    return dp[key];
}
 
// Wrapper function to initiate
// the solving process
int removeNumbers(vector<int>& numbers)
{
    return solver(numbers, 0, numbers.size() - 1, 0);
}
 
// Drivers code
int main()
{
    // Example usage
    vector<int> numbers = { 1, 2, 3, 2, 1, 1, 1 };
 
    // Drivers code
    cout << removeNumbers(numbers);
    return 0;
}


Java




// Java Implementation
 
import java.util.HashMap;
import java.util.Map;
 
public class Main {
    // Memoization map to store computed results
    static Map<Integer, Integer> dp = new HashMap<>();
 
    // Recursive function to solve the problem
    public static int solver(int[] numbers, int l, int r, int k) {
        // Base case: if left index exceeds right index, return 0
        if (l > r) {
            return 0;
        }
 
        // Compute a unique key for memoization
        int key = (l * numbers.length + r) * numbers.length + k;
 
        // Check if result for current state is already computed
        if (dp.containsKey(key)) {
            return dp.get(key);
        }
 
        // Handle consecutive repeated numbers at the right end of the range
        while (r > l && numbers[r] == numbers[r - 1]) {
            r--;
            k++;
        }
 
        // Handle consecutive repeated numbers at the left end of the range
        while (r > l && numbers[l] == numbers[r]) {
            l++;
            k++;
        }
 
        // Update key after handling consecutive repeats
        key = (l * numbers.length + r) * numbers.length + k;
 
        // Calculate result by recursively solving subproblems
        dp.put(key, solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1));
 
        for (int i = l; i < r; i++) {
            if (numbers[i] == numbers[r]) {
                while (i + 1 < r && numbers[i + 1] == numbers[r]) {
                    i++;
                }
                // After taking similar characters from left, let the left range be
                // part of the right range and hence add K+1 to previously going on
                // range, and for the range that is left, start from k=0 and try
                // removing new subranges of similar numbers from this range.
                dp.put(key, Math.max(dp.get(key), solver(numbers, l, i, k + 1) + solver(numbers, i + 1, r - 1, 0)));
            }
        }
 
        // Return the computed result for this state
        return dp.get(key);
    }
 
    // Wrapper function to initiate the solving process
    public static int removeNumbers(int[] numbers) {
        return solver(numbers, 0, numbers.length - 1, 0);
    }
 
    // Drivers code
    public static void main(String[] args) {
        // Example usage
        int[] numbers = {1, 2, 3, 2, 1, 1, 1};
         
        // Print the result
        System.out.println(removeNumbers(numbers));
    }
}
 
// This code is contributed by Sakshi


Python3




def solver(numbers, l, r, k, dp):
    # Base case: if left index exceeds right index, return 0
    if l > r:
        return 0
 
    # Compute a unique key for memoization
    key = (l * len(numbers) + r) * len(numbers) + k
 
    # Check if the result for the current state is already computed
    if key in dp:
        return dp[key]
 
    # Handle consecutive repeated numbers at the right end of the range
    while r > l and numbers[r] == numbers[r - 1]:
        r -= 1
        k += 1
 
    # Handle consecutive repeated numbers at the left end of the range
    while r > l and numbers[l] == numbers[r]:
        l += 1
        k += 1
 
    # Update key after handling consecutive repeats
    key = (l * len(numbers) + r) * len(numbers) + k
 
    # Calculate the result by recursively solving subproblems
    dp[key] = solver(numbers, l, r - 1, 0, dp) + (k + 1) * (k + 1)
 
    for i in range(l, r):
        if numbers[i] == numbers[r]:
            while i + 1 < r and numbers[i + 1] == numbers[r]:
                i += 1
            # After taking similar characters from the left,
            # let the left range be part of the right range, and hence,
            # add K+1 to the previously going on range,
            # and for the range that is left, start from k=0 and try
            # removing new sub-ranges of similar numbers from this range.
            dp[key] = max(dp[key], solver(numbers, l, i, k + 1, dp) +
                          solver(numbers, i + 1, r - 1, 0, dp))
 
    # Return the computed result for this state
    return dp[key]
 
# Wrapper function to initiate the solving process
def remove_numbers(numbers):
    dp = {}
    return solver(numbers, 0, len(numbers) - 1, 0, dp)
 
# Example usage
numbers = [1, 2, 3, 2, 1, 1, 1]
print(remove_numbers(numbers))


C#




using System;
using System.Collections.Generic;
 
class Program
{
    static Dictionary<int, int> dp = new Dictionary<int, int>();
 
    static int Solver(List<int> numbers, int l, int r, int k)
    {
        // Base case: if left index exceeds
        // right index, return 0
        if (l > r)
            return 0;
 
        // Compute a unique key for memoization
        int key = ((l * numbers.Count + r) * numbers.Count) + k;
 
        // Check if result for current state
        // is already computed
        if (dp.ContainsKey(key))
            return dp[key];
 
        // Handle consecutive repeated numbers
        // at the right end of the range
        while (r > l && numbers[r] == numbers[r - 1])
        {
            r--;
            k++;
        }
 
        // Handle consecutive repeated numbers
        // at the left end of the range
        while (r > l && numbers[l] == numbers[r])
        {
            l++;
            k++;
        }
 
        // Update key after handling
        // consecutive repeats
        key = ((l * numbers.Count + r) * numbers.Count) + k;
 
        // Calculate result by recursively
        // solving subproblems
        dp[key] = Solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1);
 
        for (int i = l; i < r; i++)
        {
            if (numbers[i] == numbers[r])
            {
                while (i + 1 < r && numbers[i + 1] == numbers[r])
                    i++;
                // After taking similar characters
                // from left let the left range be
                // part of the right range and hence
                // add K+1 to previously going on
                // range, and for the range that is
                // left start from k=0 and try
                // removing new subranges of similar
                // numbers from this range.
                dp[key] = Math.Max(dp[key], Solver(numbers, l, i, k + 1) + Solver(numbers, i + 1, r - 1, 0));
            }
        }
 
        // Return the computed result
        // for this state
        return dp[key];
    }
 
    // Wrapper function to initiate
    // the solving process
    static int RemoveNumbers(List<int> numbers)
    {
        return Solver(numbers, 0, numbers.Count - 1, 0);
    }
 
    // Driver code
    static void Main()
    {
        // Example usage
        List<int> numbers = new List<int> { 1, 2, 3, 2, 1, 1, 1 };
 
        // Driver code
        Console.WriteLine(RemoveNumbers(numbers));
    }
}
 
// This code is contributed by shivamgupta310570


Javascript




// Using a Map for memoization
let dp = new Map();
 
// Recursive function to solve the problem
function solver(numbers, l, r, k) {
    // Base case: if left index exceeds right index, return 0
    if (l > r) {
        return 0;
    }
 
    // Compute a unique key for memoization
    let key = (l * numbers.length + r) * numbers.length + k;
 
    // Check if result for current state is already computed
    if (dp.has(key)) {
        return dp.get(key);
    }
 
    // Handle consecutive repeated numbers at the right end of the range
    while (r > l && numbers[r] === numbers[r - 1]) {
        r--;
        k++;
    }
 
    // Handle consecutive repeated numbers at the left end of the range
    while (r > l && numbers[l] === numbers[r]) {
        l++;
        k++;
    }
 
    // Update key after handling consecutive repeats
    key = (l * numbers.length + r) * numbers.length + k;
 
    // Calculate result by recursively solving subproblems
    dp.set(key, solver(numbers, l, r - 1, 0) + (k + 1) * (k + 1));
 
    for (let i = l; i < r; i++) {
        if (numbers[i] === numbers[r]) {
            while (i + 1 < r && numbers[i + 1] === numbers[r]) {
                i++;
            }
            // After taking similar characters from left, let the left range be
            // part of the right range and hence add K+1 to previously going on
            // range. For the range that is left, start from k=0 and try
            // removing new sub-ranges of similar numbers from this range.
            dp.set(key, Math.max(dp.get(key), solver(numbers, l, i, k + 1) + solver(numbers, i + 1, r - 1, 0)));
        }
    }
 
    // Return the computed result for this state
    return dp.get(key);
}
 
// Wrapper function to initiate the solving process
function removeNumbers(numbers) {
    return solver(numbers, 0, numbers.length - 1, 0);
}
 
// Example usage
let numbers = [1, 2, 3, 2, 1, 1, 1];
console.log(removeNumbers(numbers));
 
// This code is contributed by shivamgupta0987654321


Output

21












Time Complexity: O(n^4), where n is the size of the input vector numbers, we can see that the function is called twice in the for loop , that makes O(n^2)^2 times, which is why O(n^4).
Auxiliary Space: O(n^3). This is because of the usage of the unordered map dp for memoization.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads