Palindrome Algorithm Questions

Given a string which we can delete at most k, return whether you can make a palindrome.

For example, given 'waterrfetawx' and a k of 2, you could delete f and x to get 'waterretaw'.

Notice the recursive structure of this problem:

  • If the string is already a palindrome, then we can just return true.
  • If the string is not already a palindrome, then try getting rid of the first or last character that does not contribute to its palindromicity.

This takes O(2min(n, k)) time, where n is the length of the original string. This is because we call k_palindrome twice on each subproblem, and on each call, either k or the string gets reduced by at least 1.

class Solution {   
    public boolean canMakePalindrome(String s, int k) {
        return canMakePalindromeHelper(s, 0, s.length() - 1, k);
    }

    private boolean canMakePalindromeHelper(String s, int start, int end, int k) {
        while(start < end) {
            if(s.charAt(start) == s.charAt(end)) {
                start++;
                end--;
            }
            else {
                break;
            }
        }
        if(start == end) {
            return true;
        }
        if(k == 0) {
            return false;
        }
        return canMakePalindromeHelper(s, start + 1, end, k - 1) || canMakePalindromeHelper(s, start, end - 1, k - 1);
    }
}

Dynamic Programming, O(n^2 * k) runtime, O(n^2 * k) space

dp[i][j][k]:  if the substring s[i - j] can make a palindrome with at most k deletions. 

dp[i][j][k] = dp[i + 1][j - 1][k] if s[i] == s[j];  dp[i][j][k] = dp[i + 1][j][k - 1] || dp[i][j - 1][k - 1] if s[i] != s[j];

substring of length 1 and 2 needs to be initialized first.

class Solution {    
    public boolean canMakePalindrome(String s, int k) {
        int n = s.length();
        boolean[][][] dp = new boolean[n][n][k + 1];
        for(int i = 0; i < n; i++) {
            for(int del = 0; del <= k; del++) {
                dp[i][i][del] = true;
            }
        }
        for(int startIdx = 0; startIdx <= n - 2; startIdx++) {
            int endIdx = startIdx + 1;
            for(int del = 0; del <= k; del++) {
                if(del == 0 && s.charAt(startIdx) == s.charAt(endIdx) || del > 0) {
                    dp[startIdx][endIdx][del] = true;
                }
            }
        }
        for(int del = 0; del <= k; del++) {
            for(int l = 3; l <= n; l++) {
                for(int startIdx = 0; startIdx <= n - l; startIdx++) {
                    int endIdx = startIdx + l - 1;
                    if(startIdx == 1 && endIdx == 9 && del == 1) {
                        System.out.println();
                    }
                    if(s.charAt(startIdx) == s.charAt(endIdx)) {
                        dp[startIdx][endIdx][del] = dp[startIdx + 1][endIdx - 1][del];
                    }
                    else if(del > 0) {
                        dp[startIdx][endIdx][del] = (dp[startIdx + 1][endIdx][del - 1] || dp[startIdx][endIdx - 1][del - 1]);
                    }
                }
            }
        }
        for(int del = 0; del <= k; del++) {
            if(dp[0][n - 1][del]) {
                return true;
            }
        }
        return false;
    }
}

Dynamic Programming, convert to the Longest Palindromic Subsequence problem.  O(n^2) runtime, O(n^2) space 

Find the length of the longest palindromic subsequence using the same optimal substructure, let it be longest. If s.length() - longest <= k, then return true, otherwise return false.

class Solution {
    public boolean canMakePalindrome(String s, int k) {
        int longest = longestPalindromeSubseq(s);
        return s.length() - longest <= k;
    }

    private int longestPalindromeSubseq(String s) {
        if(s == null || s.length() == 0){
            return 0;
        }
        int n = s.length();
        int[][] dp = new int[n][n];
        for(int i = 0; i < n; i++){
            dp[i][i] = 1;
        }
        for(int i = 0; i < n - 1; i++){
            dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1) ? 2 : 1;
        }
        for(int l = 3; l <= n; l++){
            for(int startIdx = 0; startIdx <= n - l; startIdx++){
                int endIdx = startIdx + l - 1;
                if(s.charAt(startIdx) == s.charAt(endIdx)){
                    dp[startIdx][endIdx] = dp[startIdx + 1][endIdx - 1] + 2;
                }
                else{
                    dp[startIdx][endIdx] = Math.max(dp[startIdx + 1][endIdx], dp[startIdx][endIdx - 1]);
                }
            }
        }
        return dp[0][n - 1];
    }
}

LeetCode 1147. Longest Chunked Palindrome Decomposition

Return the largest possible k such that there exists a_1, a_2, ..., a_k such that:

  • Each a_i is a non-empty string;
  • Their concatenation a_1 + a_2 + ... + a_k is equal to text;
  • For all 1 <= i <= k,  a_i = a_{k+1 - i}.

Example 1:

Input: text = "ghiabcdefhelloadamhelloabcdefghi"
Output: 7
Explanation: We can split the string on "(ghi)(abcdef)(hello)(adam)(hello)(abcdef)(ghi)".

Example 2:

Input: text = "merchant"
Output: 1
Explanation: We can split the string on "(merchant)".

Example 3:

Input: text = "antaprezatepzapreanta"
Output: 11
Explanation: We can split the string on "(a)(nt)(a)(pre)(za)(tpe)(za)(pre)(a)(nt)(a)".

Example 4:

Input: text = "aaa"
Output: 3
Explanation: We can split the string on "(a)(a)(a)".

Constraints:

  • text consists only of lowercase English characters.
  • 1 <= text.length <= 1000

1. O(N^3) Dp solution

dp[i][j]: the longest chunked palindrome decomposition for text[i, j].

dp[i][j] = Max of {1, max of {dp[i1 + 1][j1 - 1] + 2, for all text[i, i1] == text[j1, j] and i1 < j1}}

Init: for all dp[i][j] where i <= j, dp[i][j] = 1, we can always get at least 1 if we don't do any split. If i > j, dp[i][j] = 0, meaning there is no middle substring left after removing palindrome chunk from both sides.

Answer: dp[0][N - 1].

class Solution {
    public int longestDecomposition(String text) {
        int n = text.length();
        int[][] dp = new int[n][n];
        int leftEnd = n % 2 != 0 ? n / 2 : n / 2 - 1;
        int rightStart = n / 2;
        for(int i = leftEnd, j = rightStart; i >= 0 && j < n; i--, j++) {
            dp[i][j] = 1;
            for(int i1 = i, j1 = j; i1 < j1; i1++, j1--) {
                if(text.substring(i, i1 + 1).equals(text.substring(j1, j + 1))) {
                    dp[i][j] = Math.max(dp[i1 + 1][j1 - 1] + 2, dp[i][j]);
                }
            }
        }
        return dp[0][n - 1];
    }
}
原文地址:https://www.cnblogs.com/lz87/p/10619175.html