class Solution:
def minimumMoves(self, arr):
n = len(arr)
dp = [[0] * n for _ in range(n)]
for i in range(n):
dp[i][i] = 1
for length in range(2, n + 1):
for i in range(n - length + 1):
j = i + length - 1
if arr[i] == arr[j]:
if length == 2:
dp[i][j] = 1
else:
dp[i][j] = dp[i+1][j-1]
else:
dp[i][j] = float('inf')
for k in range(i, j):
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j])
return dp[0][n-1]
class Solution {
public:
int minimumMoves(vector<int>& arr) {
int n = arr.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int i = 0; i < n; ++i) dp[i][i] = 1;
for (int len = 2; len <= n; ++len) {
for (int i = 0; i + len - 1 < n; ++i) {
int j = i + len - 1;
if (arr[i] == arr[j]) {
if (len == 2) dp[i][j] = 1;
else dp[i][j] = dp[i+1][j-1];
} else {
dp[i][j] = INT_MAX;
for (int k = i; k < j; ++k)
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]);
}
}
}
return dp[0][n-1];
}
};
class Solution {
public int minimumMoves(int[] arr) {
int n = arr.length;
int[][] dp = new int[n][n];
for (int i = 0; i < n; i++) dp[i][i] = 1;
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len - 1 < n; i++) {
int j = i + len - 1;
if (arr[i] == arr[j]) {
if (len == 2) dp[i][j] = 1;
else dp[i][j] = dp[i+1][j-1];
} else {
dp[i][j] = Integer.MAX_VALUE;
for (int k = i; k < j; k++)
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k+1][j]);
}
}
}
return dp[0][n-1];
}
}
var minimumMoves = function(arr) {
const n = arr.length;
const dp = Array.from({length: n}, () => Array(n).fill(0));
for (let i = 0; i < n; ++i) dp[i][i] = 1;
for (let len = 2; len <= n; ++len) {
for (let i = 0; i + len - 1 < n; ++i) {
let j = i + len - 1;
if (arr[i] === arr[j]) {
if (len === 2) dp[i][j] = 1;
else dp[i][j] = dp[i+1][j-1];
} else {
dp[i][j] = Infinity;
for (let k = i; k < j; ++k)
dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k+1][j]);
}
}
}
return dp[0][n-1];
};
You are given an array of positive integers, arr
. In one move, you can remove any contiguous subarray that forms a palindrome (reads the same forward and backward). After removal, the left and right parts of the array concatenate to form a new array. Your goal is to find the minimum number of moves required to remove the entire array.
Constraints:
At first glance, the problem seems to suggest checking every possible way to remove palindromic subarrays, which could be a huge number of combinations. A brute-force approach would try all possible ways to remove palindromes, but this quickly becomes infeasible for larger arrays due to the exponential number of possibilities.
Instead, we should look for overlapping subproblems and optimal substructure—classic signs that dynamic programming (DP) can help. If we know the minimum moves needed to remove a subarray, we can use that information to solve larger subarrays. The key is to break the problem into manageable pieces where we can reuse previous results.
We also notice that if the first and last elements of a subarray are equal, and the subarray between them can be removed in some way, then the whole subarray could potentially be removed as a palindrome in one move. This insight helps us design a DP solution.
We use dynamic programming to solve this problem efficiently. Here’s how the algorithm works, step by step:
dp[i][j]
represent the minimum number of moves required to remove the subarray from index i
to j
(inclusive).i == j
(the subarray has only one element), it is always a palindrome, so dp[i][i] = 1
.arr[i] == arr[j]
, then the minimum moves for dp[i][j]
can be the same as dp[i+1][j-1]
(removing the inner subarray, then removing the matching ends together).k
between i
and j
, and take the minimum:
dp[i][j] = min(dp[i][k] + dp[k+1][j])
for all k
in [i, j-1]
.dp
table for all subarrays of increasing length (from 1 up to n).dp[0][n-1]
, representing the minimum moves to remove the entire array.This approach ensures that we always use the best way to remove palindromic subarrays, and we avoid recalculating the same subproblems multiple times.
Let's consider arr = [1, 3, 4, 1, 5]
.
dp[i][i] = 1
for all i
.
[1,3]
, [3,4]
, [4,1]
, [1,5]
are not palindromes, so each needs 2 moves.[1,3,4]
, [3,4,1]
, [4,1,5]
are not palindromes, so try all possible splits. Each split gives 3 moves, but we check for better options.[1,3,4,1]
(indices 0 to 3):
[3,4]
can be removed in one move (it's not a palindrome), so dp[1][2]=2
. Thus, dp[0][3]=dp[1][2]=2
.[1,3,4,1,5]
:
dp[0][0] + dp[1][4] = 1 + ?
dp[0][1] + dp[2][4] = 2 + ?
dp[0][2] + dp[3][4] = 3 + ?
dp[0][3] + dp[4][4] = 2 + 1 = 3
So, the minimum moves to remove the array [1, 3, 4, 1, 5]
is 3.
Brute-force approach: Would try every possible order of removing palindromic subarrays, which has exponential time complexity (much greater than O(2^n)), making it infeasible for larger arrays.
Dynamic Programming approach:
This is efficient enough for n up to 100.
The Palindrome Removal problem can be solved efficiently using dynamic programming by carefully defining subproblems (dp[i][j]
) and leveraging the properties of palindromes. We avoid brute-force enumeration by reusing solutions to subarrays, and we always choose the optimal split at each step. This results in a clean, elegant, and efficient O(n3) solution that demonstrates the power of DP for problems with overlapping subproblems and optimal substructure.