CLOSE
🛠️ Settings

Problem Statement

 Given a string s and an integer k, return true if the string can be a palindrome after deleting at most k characters.

Constraints


1 <= s.length <= 1000
s has only lowercase English letters.
1 <= k <= s.length

Examples

Example 1:

Input: s = "abcdeca", k = 2
Output: true
Explanation: Remove 'b' and 'e' characters.

Different Approaches

1️⃣ Recursion (Brute Force Approach)

🔍 Idea:

  • Try all possible ways to delete characters and check if any lead to a palindrome. At each mismatch, recursively try:
  • deleting the left character
  • deleting the right character

Code:

// Function to check if a string can be a palindrome 
// by removing at most 'k' characters between indices i and j
bool isPalindrome(string& s, int i, int j, int k) {

    // Base case: if we've used more deletions than allowed (k < 0), it's not valid
    if (k < 0) return false;

    // Loop from both ends towards the center
    while (i < j) {

        // If characters at current positions don't match
        if (s[i] != s[j]) {
            // Try both options:
            // 1. Remove character at the left (i) and decrease k
            // 2. Remove character at the right (j) and decrease k
            // If either leads to a valid palindrome, return true
            return isPalindrome(s, i + 1, j, k - 1) || isPalindrome(s, i, j - 1, k - 1);
        }

        // If characters match, move both pointers inward
        i++;
        j--;
    }

    // If loop completes, it means it's a palindrome within the allowed deletions
    return true;
}

Complexity Analysis:

  • Time Complexity:O(2 ^ n)
  • Space Complexity:O(n)
    • Recursion call stack

2️⃣ Memoization

🔍 Idea:

  • Optimize brute force by storing results for (i, j, k) in a DP table to avoid re-computation.

Code:

class Solution {
public:
    // Helper function to check if the substring s[left...right]
    // can be a palindrome by removing at most 'remainingDeletes' characters
    bool canBePalindrome(string& s, int left, int right, int remainingDeletes, vector<vector<int>>& dp) {
        // Base Case: If left crosses or meets right, it's already a palindrome
        if (left >= right) return true;

        // If we've already computed the result for this substring, return it
        if (dp[left][right] != -1) return dp[left][right];

        // If characters match, move inward
        if (s[left] == s[right]) {
            dp[left][right] = canBePalindrome(s, left + 1, right - 1, remainingDeletes, dp);
        } else {
            // If characters don't match and we have no deletions left
            if (remainingDeletes == 0) {
                dp[left][right] = false;
            } else {
                // Try removing one character either from left or right
                bool removeLeft = canBePalindrome(s, left + 1, right, remainingDeletes - 1, dp);
                bool removeRight = canBePalindrome(s, left, right - 1, remainingDeletes - 1, dp);
                dp[left][right] = removeLeft || removeRight;
            }
        }

        return dp[left][right];
    }

    // Main function to check if string can be palindrome by removing at most k characters
    bool isValidPalindrome(string s, int k) {
        int n = s.size();

        // dp[i][j] stores whether s[i...j] can be a palindrome with at most k deletions
        vector<vector<int>> dp(n, vector<int>(n, -1));  // -1 means not computed yet

        // Call the helper from the full string with k deletions allowed
        return canBePalindrome(s, 0, n - 1, k, dp);
    }
};

Complexity Analysis:

  • Time Complexity:O(n^2)
  • Space Complexity: O(n^2)
    • Because of the recursion stack and dp array