1. 4D Memoized State DP
Intuition
Define dp(i, k, lastChar, count): option 1 is deleting s[i] (dp(i + 1, k - 1, lastChar, count)). Option 2 is keeping s[i]: if s[i] == lastChar, the cost increases if count transitions 1->2, 9->10, or 99->100. If s[i] != lastChar, cost increases by 1 for the new character.
When to apply: Direct state formulation.
Algorithm
- Initialize 4D memo table.
- Helper
solve(i, k, lastChar, count): - If
k < 0: return infinity. - If
i == s.length: return 0. - Option 1 (delete
s[i]):res = solve(i + 1, k - 1, lastChar, count). - Option 2 (keep
s[i]): compute length delta and recursesolve(i + 1, k, s[i], newCount). - Return minimum.
#include <iostream>#include <string>#include <vector>#include <algorithm>#include <cstring> class Solution { int memo[105][105][27][105]; int solve(int i, int k, int lastChar, int count, const std::string& s) { if (k < 0) return 1e9; if (i == (int)s.size()) return 0; if (memo[i][k][lastChar][count] != -1) return memo[i][k][lastChar][count]; // Option 1: Delete s[i] int res = solve(i + 1, k - 1, lastChar, count, s); // Option 2: Keep s[i] int curChar = s[i] - 'a' + 1; if (curChar == lastChar) { int inc = (count == 1 || count == 9 || count == 99) ? 1 : 0; res = std::min(res, inc + solve(i + 1, k, lastChar, count + 1, s)); } else { res = std::min(res, 1 + solve(i + 1, k, curChar, 1, s)); } return memo[i][k][lastChar][count] = res; }public: int getLengthOfOptimalCompression(std::string s, int k) { std::memset(memo, -1, sizeof(memo)); return solve(0, k, 0, 0, s); }}; int main() { Solution sol; std::cout << sol.getLengthOfOptimalCompression("aaabcccd", 2) << std::endl; // 4 return 0;}Time & Space Complexity
- Time complexity:
O(N * K * 26 * N)— State space size: 100 * 100 * 27 * 100. - Space complexity:
O(N * K * 26 * N)
Why Brute Force Doesn't Scale:
Anchor-based forward grouping reduces state space from 4D to 2D.
Compact O(N * K) memoization table.