Taro Logo

Longest Palindromic Subsequence After at Most K Operations

Medium
Google logo
Google
1 view
Topics:
StringsDynamic Programming

You are given a string s and an integer k.

In one operation, you can replace the character at any position with the next or previous letter in the alphabet (wrapping around so that 'a' is after 'z'). For example, replacing 'a' with the next letter results in 'b', and replacing 'a' with the previous letter results in 'z'. Similarly, replacing 'z' with the next letter results in 'a', and replacing 'z' with the previous letter results in 'y'.

Return the length of the longest palindromic subsequence of s that can be obtained after performing at most k operations.

Example 1:

Input: s = "abced", k = 2 Output: 3

Explanation:

  • Replace s[1] with the next letter, and s becomes "acced".
  • Replace s[4] with the previous letter, and s becomes "accec".

The subsequence "ccc" forms a palindrome of length 3, which is the maximum.

Example 2:

Input: s = "aaazzz", k = 4 Output: 6

Explanation:

  • Replace s[0] with the previous letter, and s becomes "zaazzz".
  • Replace s[4] with the next letter, and s becomes "zaazaz".
  • Replace s[3] with the next letter, and s becomes "zaaaaz".

The entire string forms a palindrome of length 6.

Solution


Let's analyze the problem. We are given a string s and an integer k, representing the maximum number of operations we can perform to transform the string. Each operation allows us to change a character to its adjacent character in the alphabet (wrapping around). The goal is to find the length of the longest palindromic subsequence we can obtain after performing at most k operations.

Naive Approach

A brute-force approach would involve trying all possible subsequences of the given string s. For each subsequence, we would try all possible combinations of character changes within the allowed k operations to see if we can form a palindrome. This approach is highly inefficient because generating all subsequences is exponential and then, for each subsequence, trying all possible character changes is also exponential. This approach is not feasible due to its time complexity.

Optimal Approach

A more efficient approach utilizes dynamic programming. We can create a 2D DP table where dp[i][j] represents the length of the longest palindromic subsequence of the substring s[i...j] that can be obtained with at most k operations.

  1. Base Case:
    • dp[i][i] = 1 for all i (a single character is a palindrome of length 1).
  2. Recursive Relation:
    • If s[i] == s[j]: dp[i][j] = dp[i+1][j-1] + 2
    • If abs(s[i] - s[j]) <= k: dp[i][j] = dp[i+1][j-1] + 2
    • Otherwise: dp[i][j] = max(dp[i+1][j], dp[i][j-1])

We can optimize this by considering the cost to transform s[i] into s[j] or vice versa. If the cost (absolute difference between the characters) is within our budget k, we can potentially extend our palindromic subsequence. Otherwise, we explore subsequences that exclude either s[i] or s[j].

Here's the implementation in Python:

def longest_palindrome_subsequence(s: str, k: int) -> int:
    n = len(s)
    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
            cost = abs(ord(s[i]) - ord(s[j]))
            if cost <= k:
                dp[i][j] = (dp[i + 1][j - 1] + 2) if length > 2 else 2  # Corrected
            else:
                dp[i][j] = max(dp[i + 1][j], dp[i][j - 1])

    return dp[0][n - 1]

Explanation

The core idea is to build up the dp table by considering substrings of increasing length. For each substring s[i...j], we check if s[i] and s[j] can be made equal with at most k operations. If so, we add 2 to the length of the longest palindromic subsequence of the substring s[i+1...j-1]. Otherwise, we take the maximum of the lengths of the longest palindromic subsequences of s[i+1...j] and s[i...j-1].

Time and Space Complexity

  • Time Complexity: O(n^2), where n is the length of the string s. We iterate through all possible substrings to fill the dp table.
  • Space Complexity: O(n^2), due to the dp table of size n x n.

Edge Cases

  • Empty string: The algorithm should handle an empty string gracefully (returning 0).
  • k = 0: In this case, we simply find the longest palindromic subsequence without any modifications.
  • k is very large: If k is sufficiently large, we can always make the entire string a palindrome consisting of the same character.

Example Walkthrough

Consider the example s = "abced", k = 2.

The dp table will be built as follows:

Initially, dp[i][i] = 1 for all i.

For length = 2:

  • i = 0, j = 1: cost = abs('a' - 'b') = 1 <= 2. dp[0][1] = 2
  • i = 1, j = 2: cost = abs('b' - 'c') = 1 <= 2. dp[1][2] = 2
  • i = 2, j = 3: cost = abs('c' - 'e') = 2 <= 2. dp[2][3] = 2
  • i = 3, j = 4: cost = abs('e' - 'd') = 1 <= 2. dp[3][4] = 2

For length = 3:

  • i = 0, j = 2: cost = abs('a' - 'c') = 2 <= 2. dp[0][2] = dp[1][1] + 2 = 1 + 2 = 3
  • i = 1, j = 3: cost = abs('b' - 'e') = 3 > 2. dp[1][3] = max(dp[2][3], dp[1][2]) = max(2, 2) = 2
  • i = 2, j = 4: cost = abs('c' - 'd') = 1 <= 2. dp[2][4] = dp[3][3] + 2 = 1 + 2 = 3

For length = 4:

  • i = 0, j = 3: cost = abs('a' - 'e') = 4 > 2. dp[0][3] = max(dp[1][3], dp[0][2]) = max(2, 3) = 3
  • i = 1, j = 4: cost = abs('b' - 'd') = 2 <= 2. dp[1][4] = dp[2][3] + 2 = 2 + 2= 4 if length > 2

For length = 5:

  • i = 0, j = 4: cost = abs('a' - 'd') = 3 > 2. dp[0][4] = max(dp[1][4], dp[0][3]) = max(2, 3) = 3 if length > 2

Therefore, the longest palindromic subsequence length is 3.