Want Help Cracking FAANG?

(Then click this)

×
Back to Question Bank

1717. Maximum Score From Removing Substrings - Leetcode Solution

Code Implementation

class Solution:
    def maximumGain(self, s: str, x: int, y: int) -> int:
        def remove_pattern(s, first, second, score):
            stack = []
            total = 0
            for c in s:
                if stack and stack[-1] == first and c == second:
                    stack.pop()
                    total += score
                else:
                    stack.append(c)
            return ''.join(stack), total

        res = 0
        # Remove the higher value pattern first
        if x >= y:
            s, gain = remove_pattern(s, 'a', 'b', x)
            res += gain
            s, gain = remove_pattern(s, 'b', 'a', y)
            res += gain
        else:
            s, gain = remove_pattern(s, 'b', 'a', y)
            res += gain
            s, gain = remove_pattern(s, 'a', 'b', x)
            res += gain
        return res
      
class Solution {
public:
    int maximumGain(string s, int x, int y) {
        auto removePattern = [](string &s, char first, char second, int score) {
            stack<char> stk;
            int total = 0;
            string res = "";
            for (char c : s) {
                if (!stk.empty() && stk.top() == first && c == second) {
                    stk.pop();
                    total += score;
                } else {
                    stk.push(c);
                }
            }
            // Build the remaining string
            string remain = "";
            while (!stk.empty()) {
                remain += stk.top();
                stk.pop();
            }
            reverse(remain.begin(), remain.end());
            s = remain;
            return total;
        };
        int total = 0;
        if (x >= y) {
            total += removePattern(s, 'a', 'b', x);
            total += removePattern(s, 'b', 'a', y);
        } else {
            total += removePattern(s, 'b', 'a', y);
            total += removePattern(s, 'a', 'b', x);
        }
        return total;
    }
};
      
class Solution {
    public int maximumGain(String s, int x, int y) {
        StringBuilder sb = new StringBuilder();
        int res = 0;
        if (x >= y) {
            String[] result = removePattern(s, 'a', 'b', x);
            res += Integer.parseInt(result[1]);
            result = removePattern(result[0], 'b', 'a', y);
            res += Integer.parseInt(result[1]);
        } else {
            String[] result = removePattern(s, 'b', 'a', y);
            res += Integer.parseInt(result[1]);
            result = removePattern(result[0], 'a', 'b', x);
            res += Integer.parseInt(result[1]);
        }
        return res;
    }

    private String[] removePattern(String s, char first, char second, int score) {
        StringBuilder stack = new StringBuilder();
        int total = 0;
        for (char c : s.toCharArray()) {
            if (stack.length() > 0 && stack.charAt(stack.length() - 1) == first && c == second) {
                stack.deleteCharAt(stack.length() - 1);
                total += score;
            } else {
                stack.append(c);
            }
        }
        return new String[]{stack.toString(), String.valueOf(total)};
    }
}
      
var maximumGain = function(s, x, y) {
    function removePattern(str, first, second, score) {
        let stack = [];
        let total = 0;
        for (let c of str) {
            if (stack.length > 0 && stack[stack.length - 1] === first && c === second) {
                stack.pop();
                total += score;
            } else {
                stack.push(c);
            }
        }
        return [stack.join(''), total];
    }

    let res = 0;
    if (x >= y) {
        let out = removePattern(s, 'a', 'b', x);
        s = out[0];
        res += out[1];
        out = removePattern(s, 'b', 'a', y);
        s = out[0];
        res += out[1];
    } else {
        let out = removePattern(s, 'b', 'a', y);
        s = out[0];
        res += out[1];
        out = removePattern(s, 'a', 'b', x);
        s = out[0];
        res += out[1];
    }
    return res;
};
      

Problem Description

Given a string s and two integers x and y, you are allowed to repeatedly remove either the substring "ab" from s for x points or the substring "ba" for y points. Each removal shortens the string, and you may continue removing as long as one of these substrings exists. Your task is to maximize the total score by strategically removing substrings in the best order.

  • Each removal is independent and may affect subsequent removals.
  • You must choose the order of substring removals to maximize your score.
  • Constraints: 1 <= s.length <= 10^5, 1 <= x, y <= 10^4.

Thought Process

At first glance, it might seem that removing substrings as you find them would be sufficient. However, because removing one pattern may create opportunities to remove the other, the order in which you remove the substrings "ab" and "ba" becomes important. If you remove the higher-value pattern first, you may maximize your score, as lower-value removals could block higher-value ones if done prematurely.

A brute-force approach would try all possible orders, but with string lengths up to 100,000, that's computationally infeasible. Instead, we need an efficient way to simulate removals and ensure we always prioritize the most valuable options.

Solution Approach

  • Step 1: Prioritize High-Value Removals
    • First, determine which substring removal is worth more: "ab" or "ba".
    • Remove all occurrences of the higher-value substring first, as this will maximize your score potential.
  • Step 2: Use a Stack to Simulate Removals
    • Iterate through the string, using a stack to efficiently track and remove adjacent character pairs.
    • When you encounter a pair matching the target substring, pop the previous character and increase your score.
    • Continue until the entire string has been processed for the current pattern.
  • Step 3: Remove the Second Pattern
    • After finishing with the higher-value pattern, repeat the process for the remaining string and the other pattern.
  • Step 4: Return the Total Score
    • Add up the scores from both removal passes and return the result.

This approach ensures that the most valuable removals are done first, and the stack-based simulation allows for linear-time processing, which is essential for large input sizes.

Example Walkthrough

Input: s = "cdbababc", x = 4, y = 5

  1. Since y > x, we remove "ba" (worth 5 points) first.
  2. Traverse the string:
    • At index 3-4, find "ba": remove it, score = 5. New string: "cdababc" -> "cdabc"
    • At index 2-3, find "ba" again: remove it, score = 5 + 5 = 10. New string: "cdabc" -> "cdc"
  3. Now remove "ab" (worth 4 points):
    • No "ab" found in "cdc". So score remains 10.
  4. Final Score: 10

The order of removal affects the outcome; had we removed "ab" first, we would have scored less.

Time and Space Complexity

  • Brute-force Approach:
    • Trying all possible removal orders is exponential in time and not feasible for large strings.
  • Optimized Stack-Based Approach:
    • Time Complexity: O(n), where n is the length of the string. Each character is processed at most twice (once per pattern).
    • Space Complexity: O(n), for the stack used to simulate removals.

Summary

To maximize the score from removing substrings in this problem, it's crucial to always remove the higher-value pattern first. By simulating removals with a stack, we ensure linear-time processing and avoid missing valuable removal opportunities. This strategy leverages the greedy principle and efficient data structures, making the solution both elegant and practical for large inputs.