Longest Palindromic Subsequence Pattern

Problems Following Palindromic Subsequence Pattern

1. Longest Palindromic Subsequence

Problem Statement:

Given a sequence, find the length of longest palindromic subsequence (LPS).

In Palindromic subsequence, elements read the same backword and forward.

A subsequence is a sequence that can be derived from anothe subsequence by deleting some or no elements without changing the order of remaining elements.

======= Examples: =======
Input: "abdbca"
Output: 5 => "abdba"

Input: "cddpd"
Output: 3 => "ddd"

Input: "pqr"
Output: 1 => any single char "p" or "q" or "r"


Brute-Force : Recursive Solution

Code:

def find_lps_length_recursion(string):
    start, end = 0, len(string) - 1
    return find_lps_length_recursion_util(string, start, end)


def find_lps_length_recursion_util(string, start, end):
    if (start > end):
        return 0

    if (start == end):
        return 1

    if (string[start] == string[end]):
        return 2 + find_lps_length_recursion_util(string, start + 1, end - 1)
    else:
        skip_start_lps = find_lps_length_recursion_util(string, start + 1, end)
        skip_end_lps = find_lps_length_recursion_util(string, start, end - 1)
        return max(skip_start_lps, skip_end_lps)


print("Recursive Method :")
print(find_lps_length_recursion("abdbca"))
print(find_lps_length_recursion("cddpd"))
print(find_lps_length_recursion("pqr"))

Output:

Recursive Method :
5
3
1

Complexity:


DP : Recursion + Memoization (Top-Down) Solution

Code:

def find_lps_length_dp_memoization(string):
    n = len(string)
    start, end = 0, n - 1
    memory = [[None]*n for i in range(n)]
    return find_lps_length_memoization_util(string, start, end, memory)


def find_lps_length_memoization_util(string, start, end, memory):
    if (start > end):
        return 0

    if (start == end):
        return 1

    if (memory[start][end]):
        return memory[start][end]

    if (string[start] == string[end]):
        memory[start][end] = 2 + find_lps_length_memoization_util(string, start + 1, end - 1, memory)
        return memory[start][end]

    skip_start_lps = find_lps_length_memoization_util(string, start + 1, end, memory)
    skip_end_lps = find_lps_length_memoization_util(string, start, end - 1, memory)
    memory[start][end] = max(skip_start_lps, skip_end_lps)
    return memory[start][end]


print("\nDP -> Recursion + Memoization Method :")
print(find_lps_length_dp_memoization("abdbca"))
print(find_lps_length_dp_memoization("cddpd"))
print(find_lps_length_dp_memoization("pqr"))

Output:

DP -> Recursion + Memoization Method :
5
3
1

Complexity:


DP : Iteration + Tabulation (Bottom-Up) Solution

Table Filling Formula:

if string[start] == string[end]:

​ table[start][end] = 2 + table[start+1][end-1]

else:

​ table[start][end] = max(table[start+1][end], table[start][end-1])

Tabulation:

Final Table:

Code:

def find_lps_length_dp_tabulation(string):
    n = len(string)
    table = [[0] * n for _ in range(n)]

    # When start and end indexes are equal palindrome length will be 1
    for i in range(n):
        table[i][i] = 1

    # for filling table[start][end], we need to have table[start+1][end-1] ready
    # Also, we need to fill only top right part of table coz bottom left will always be zero (start < end)
    # Diaognal values will be 1, filled earlier
    for start in range(n - 2, -1, -1):
        for end in range(start + 1, n):
            if (string[start] == string[end]):
                table[start][end] = 2 + table[start + 1][end - 1]
            else:
                table[start][end] = max(table[start + 1][end], table[start][end - 1])

    return table[0][n-1]


print("\nDP -> Iteration + Tabulation Method :")
print(find_lps_length_dp_tabulation("abdbca"))
print(find_lps_length_dp_tabulation("cddpd"))
print(find_lps_length_dp_tabulation("pqr"))

Output:

DP -> Iteration + Tabulation Method :
5
3
1

Complexity:



2. Longest Palindromic Substring

Problem Statement:

Given a string, find the length of its longest palindromic substring.

A substring is a continuous part of string and palindromic substring, elements read the same backword and forward.

======= Examples: =======
Input: "abdbca"
Output: 3 => "bdb"

Input: "cddpd"
Output: 3 => "dpd"

Input: "pqr"
Output: 1 => any single char "p" or "q" or "r"


Brute-Force : Recursive Solution

Code:

def find_lps_length_recursion(string):
    start, end = 0, len(string) - 1
    return find_lps_length_recursion_util(string, start, end)


def find_lps_length_recursion_util(string, start, end):
    if (start > end):
        return 0

    if (start == end):
        return 1

    if (string[start] == string[end]):
        remainingLength = end - start - 1
        if (remainingLength == find_lps_length_recursion_util(string, start + 1, end - 1)):
            return 2 + remainingLength

    skip_start_lps = find_lps_length_recursion_util(string, start + 1, end)
    skip_end_lps = find_lps_length_recursion_util(string, start, end - 1)
    return max(skip_start_lps, skip_end_lps)


print("Recursive Method :")
print(find_lps_length_recursion("abdbca"))
print(find_lps_length_recursion("cddpd"))
print(find_lps_length_recursion("pqr"))

Output:

Recursive Method :
3
3
1

Complexity:


DP : Recursion + Memoization (Top-Down) Solution

Code:

def find_lps_length_dp_memoization(string):
    n = len(string)
    start, end = 0, n - 1
    memory = [[None]*n for _ in range(n)]
    return find_lps_length_dp_memoization_util(string, start, end, memory)


def find_lps_length_dp_memoization_util(string, start, end, memory):
    if (start > end):
        return 0

    if (start == end):
        return 1

    if (memory[start][end]):
        return memory[start][end]

    if (string[start] == string[end]):
        remainingLength = end - start - 1
        if (remainingLength == find_lps_length_dp_memoization_util(string, start + 1, end - 1, memory)):
            memory[start][end] = 2 + remainingLength
            return memory[start][end]

    skip_start_lps = find_lps_length_dp_memoization_util(string, start + 1, end, memory)
    skip_end_lps = find_lps_length_dp_memoization_util(string, start, end - 1, memory)
    memory[start][end] = max(skip_start_lps, skip_end_lps)
    return memory[start][end]


print("\nDP -> Recursion + Memoization Method :")
print(find_lps_length_dp_memoization("abdbca"))
print(find_lps_length_dp_memoization("cddpd"))
print(find_lps_length_dp_memoization("pqr"))

Output:

DP -> Recursion + Memoization Method :
3
3
1

Complexity:


DP : Iteration + Tabulation (Bottom-Up) Solution

Table Filling Formula:

if(string[start] == string[end]):

​ if( remaining string is of zero length or remaing string is a palindrome i.e. table[start+1][end-1] == True):

​ table[start][end] = True

Tabulation:

Final Table:

Code:

def find_lps_length_dp_tabulation(string):
    n = len(string)
    table = [[False] * n for _ in range(n)]

    # Substring of length-1 will always be palindrome
    for i in range(n):
        table[i][i] = True

    lps_length = 1
    for start in range(n - 2, -1, -1):
        for end in range(start + 1, n):
            # If char at start and index is same
            if (string[start] == string[end]):
                # If remaining string is of zero length or it is a palindrome
                if (end - start == 1 or table[start + 1][end - 1]):
                    table[start][end] = True
                    lps_length = max(lps_length, end - start + 1)

    return lps_length


print("\nDP -> Iteration + Tabulation Method :")
print(find_lps_length_dp_tabulation("abdbca"))
print(find_lps_length_dp_tabulation("cddpd"))
print(find_lps_length_dp_tabulation("pqr"))

Output:

DP -> Iteration + Tabulation Method :
3
3
1

Complexity:

Manacher Algorithm:



3. Count of Palindromic Substrings

4. Minimum Deletions in a String to make it a Palindrome

5. Palindromic Partitioning




← Previous: Unbounded Knapsack Pattern

Next: Longest Common Substring Pattern →