|
| 1 | +# [Problem 1717: Maximum Score From Removing Substrings](https://leetcode.com/problems/maximum-score-from-removing-substrings/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +- I think all of the examples could be solved using something like: |
| 5 | + - prioritize `'ab'` if `x > y` and `'ba'` otherwise |
| 6 | + - remove the higher-priority pattern (tracking points) until there are none left |
| 7 | + - then remove the lower-priority pattern (tracking points) until there are none left |
| 8 | +- For trickier inputs, it's possible that new instances of the higher-priority pattern could emerge only after removing the lower-priority patterns. In fact, maybe this could happen even after just a single lower-priority pattern removal. So maybe we need to do something like: |
| 9 | + - loop until nothing else can be done: |
| 10 | + - remove higher priority patterns until there are none left |
| 11 | + - remove a single lower priority pattern |
| 12 | + - if none found, return the current score |
| 13 | + - return the current score |
| 14 | +- This is very inefficient: finding substrings requires looping through all of `s` in $O(n)$ time, where $n$ is the length of `s`, every time we search for another instance. In the worst case (e.g., something like `abababababab...ababab` it'll take $O(n^2)$ time, which may be too long given that `s` can be up to $10^5$ characters long |
| 15 | +- One potential way to optimize would be to break the string into segments bounded by non-a/b characters. Then we could compute the max score for each of these parts and sum them together. |
| 16 | +- Another thing we can do to optimize would be to replace every consecutive sequence of non-a/b characters with a single non-a/b character (e.g., "x"). That might substantially shorten the total length of the string. |
| 17 | +- A hypothetical edge case that I'm not sure can happen (but if so, we'll need to take it into account) is if there were some case where we could get *two* matches of the lower-priority string by sacrificing *one* instance of the higher-priority string. Actually: a simple case might be: `s = 'abab'`, where `x < y`. But if `x > y/2` then our best move *isn't* to remove the middle "ba" sequence-- it's to remove each "ab" sequence in turn to cash in two instances of the lower point value to get a higher total. So those solutions above won't quite work; we need to take this into account. I wonder if we might even encounter an example where sacrificing one higher priority string could lead to *three* (or more) matches of lower-priority strings. |
| 18 | +- Let's try to think through some potential cases of what can happen with a given sequence of a/b characters: |
| 19 | + - If the length is less than or equal to two, there's nothing to search (either we have a match of one of the patterns, or not) |
| 20 | + - If the length is equal to three, we can only remove at most one substring (so if both are available we just return `max(x, y)` and otherwise we return the value for whichever string is found in the sequence, if any, or 0 otherwise). |
| 21 | + - If the length is equal to four, let's see what the possibilities are: |
| 22 | + - aaaa |
| 23 | + - aaab |
| 24 | + - aaba |
| 25 | + - aabb |
| 26 | + - abaa |
| 27 | + - abab |
| 28 | + - abba |
| 29 | + - abbb |
| 30 | + - baaa |
| 31 | + - baab |
| 32 | + - baba |
| 33 | + - babb |
| 34 | + - bbaa |
| 35 | + - bbab |
| 36 | + - bbba |
| 37 | + - bbbb |
| 38 | + - Aside: this is like counting in binary...maybe there's some shortcut we could take based on that? |
| 39 | +- after taking a pause.... |
| 40 | +- Ok, in re-thinking the "abab" example above, it's not correct as I described it. If we remove "ba" first, then we're still left with an instance of "ab" which we can use to get those points. So actually, prioritizing the higher-scoring substring *does* seem to make sense. The real issue is with efficiency. |
| 41 | +- another pause... |
| 42 | +- let's try a "stack" implementation to avoid having to loop through the same string a gajillion times: |
| 43 | + - to detect (with priority) the two-character substring `sub` with point value `p`: |
| 44 | + - loop through each character, `c` of `s`: |
| 45 | + - if the stack is not empty, and if the top of the stack + `c` matches `sub`: |
| 46 | + - increment total points by `p` |
| 47 | + - pop the top of the stack ("removes" the substring) |
| 48 | + - otherwise push `c` to the stack |
| 49 | + - return the total points, along with `''.join(stack)` (i.e., the updated string, with matches removed) |
| 50 | + - run this first for the higher-priority substring, and then for the lower-priority substring, and then return the sum of the resulting point totals |
| 51 | + |
| 52 | +## Refining the problem, round 2 thoughts |
| 53 | +- thinking through edge cases: |
| 54 | + - can we have an empty input string? no, we're given that `1 <= s.length <= 10^5` |
| 55 | + - can the "points values" (`x` or `y`) ever be negative? no, we're given that `1 <= x, y <= 10^4` |
| 56 | +- let's try this! |
| 57 | + |
| 58 | +## Attempted solution(s) |
| 59 | +```python |
| 60 | +class Solution: |
| 61 | + def maximumGain(self, s: str, x: int, y: int) -> int: |
| 62 | + def helper(s, pattern, p): |
| 63 | + stack = [] |
| 64 | + points = 0 |
| 65 | + for c in s: |
| 66 | + if len(stack) > 0 and stack[-1] + c == pattern: |
| 67 | + stack.pop() |
| 68 | + points += p |
| 69 | + else: |
| 70 | + stack.append(c) |
| 71 | + return ''.join(stack), points |
| 72 | + |
| 73 | + if x > y: |
| 74 | + high, low, p1, p2 = 'ab', 'ba', x, y |
| 75 | + else: |
| 76 | + high, low, p1, p2 = 'ba', 'ab', y, x |
| 77 | + |
| 78 | + first_pass, total1 = helper(s, high, p1) |
| 79 | + _, total2 = helper(first_pass, low, p2) |
| 80 | + |
| 81 | + return total1 + total2 |
| 82 | +``` |
| 83 | +- given test cases: pass |
| 84 | +- other tests: |
| 85 | + - 's = "ababababab"`, `x = 5`, `y = `10`: pass |
| 86 | + - 's = "ababababab"`, `x = 10`, `y = `5`: pass |
| 87 | + - `s = "abacbabcabacbabc"`, `x = 1`, `y = 1`: pass |
| 88 | +- ok, looks reasonable...submitting... |
| 89 | + |
| 90 | + |
| 91 | + |
| 92 | +done! |
| 93 | + |
0 commit comments