Find the longest palindromic substring in a given string.

Medium
8 years ago

Problem Statement:

Given a string s, your task is to identify and return the longest palindromic substring within s. A palindromic substring is a sequence of characters that reads the same forwards and backward. You may assume the maximum length of the input string s is 1000.

Example 1:

Input: s = "babad" Output: "bab" or "aba" (Both are valid as they are longest palindromic substrings of the same length.)

Example 2:

Input: s = "cbbd" Output: "bb"

Example 3:

Input: s = "a" Output: "a"

Example 4:

Input: s = "ac" Output: "a"

Considerations:

  • Efficiency: While a brute-force approach might work, aim for an algorithm with a time complexity better than O(n^3) if possible. Discuss the time and space complexity of your solution.
  • Edge Cases: How does your solution handle empty strings, single-character strings, and strings where no palindromic substring exists other than single characters?
  • Optimizations: Are there any optimizations you can apply to improve the performance of your algorithm? Think about dynamic programming or expanding around centers.
  • Code Clarity: Write clean, well-documented code that is easy to understand and maintain.
  • Testing: How would you test your solution thoroughly to ensure its correctness for various inputs, including edge cases and large strings?

Follow-up Questions (Potential):

  • Can you describe your thought process in arriving at your solution?
  • Can you explain the trade-offs between different approaches (e.g., dynamic programming vs. expanding around centers)?
  • How would you handle the case where the input string is extremely large and cannot fit into memory?
  • What are the limitations of your current solution, and how could it be further improved?

Provide a well-commented code solution and explain your reasoning. Focus on both correctness and efficiency.

Sample Answer

Longest Palindromic Substring

Problem Description

Given a string s, find the longest palindromic substring in s. Assume that the maximum length of s is 1000.

For example:

  1. If s is "babad", one possible answer is "bab". "aba" is also a valid answer.
  2. If s is "cbbd", the answer is "bb".
  3. If s is "a", the answer is "a".
  4. If s is "ac", the answer is "a".

Your solution should be efficient and handle various test cases, including empty strings and strings with a single character. A palindromic substring is a substring that reads the same forwards and backward. The goal is to identify the longest such substring within the given input string s. Consider edge cases and optimize for performance, keeping in mind that the input string's length will not exceed 1000 characters.

Brute Force Solution

A naive approach is to generate all possible substrings of the given string and check each substring for being a palindrome. We keep track of the longest palindromic substring found so far.

def is_palindrome(s):
    return s == s[::-1]

def longest_palindrome_brute_force(s):
    longest = ""
    for i in range(len(s)):
        for j in range(i, len(s)):
            substring = s[i:j+1]
            if is_palindrome(substring):
                if len(substring) > len(longest):
                    longest = substring
    return longest

Optimal Solution: Dynamic Programming

We can solve this problem more efficiently using dynamic programming. Let dp[i][j] be a boolean value indicating whether the substring s[i:j+1] is a palindrome. The base cases are substrings of length 1 and 2. Then, we can build up the dp table by checking if s[i] == s[j] and dp[i+1][j-1] is true.

def longest_palindrome_dp(s):
    n = len(s)
    if n == 0:
        return ""

    dp = [[False] * n for _ in range(n)]
    start = 0
    max_len = 1

    # Base case: single-character palindromes
    for i in range(n):
        dp[i][i] = True

    # Base case: two-character palindromes
    for i in range(n - 1):
        if s[i] == s[i+1]:
            dp[i][i+1] = True
            start = i
            max_len = 2

    # Check for palindromes of length 3 or more
    for k in range(3, n + 1):
        for i in range(n - k + 1):
            j = i + k - 1
            if s[i] == s[j] and dp[i+1][j-1]:
                dp[i][j] = True
                if k > max_len:
                    start = i
                    max_len = k

    return s[start:start + max_len]

Optimal Solution: Expand Around Center

This approach iterates through each character in the string and treats it as a potential center of a palindrome. We expand around each center to find the longest palindrome centered at that character.

def expand_around_center(s, left, right):
    while left >= 0 and right < len(s) and s[left] == s[right]:
        left -= 1
        right += 1
    return left + 1, right - 1

def longest_palindrome_expand_center(s):
    if not s:
        return ""

    start = 0
    end = 0

    for i in range(len(s)):
        # Odd length palindrome
        left1, right1 = expand_around_center(s, i, i)
        len1 = right1 - left1 + 1

        # Even length palindrome
        left2, right2 = expand_around_center(s, i, i + 1)
        len2 = right2 - left2 + 1

        if len1 > len2:
            max_len = len1
            l = left1
            r = right1
        else:
            max_len = len2
            l = left2
            r = right2

        if max_len > (end - start + 1):
            start = l
            end = r

    return s[start:end + 1]

Big(O) Run-time Analysis

Brute Force

The brute force solution has a time complexity of O(n^3), where n is the length of the string s. This is because we generate all possible substrings (O(n^2)) and then check if each substring is a palindrome (O(n)).

Dynamic Programming

The dynamic programming solution has a time complexity of O(n^2), where n is the length of the string s. This is because we fill the dp table, which takes O(n^2) time. The process of finding the longest palindrome from the table takes O(1) time.

Expand Around Center

The expand around center solution has a time complexity of O(n^2), where n is the length of the string s. This is because, in the worst case, we might need to expand from each character to the ends of the string.

Big(O) Space Usage Analysis

Brute Force

The brute force solution has a space complexity of O(1), as we are only storing the longest palindrome found so far and some temporary variables.

Dynamic Programming

The dynamic programming solution has a space complexity of O(n^2), where n is the length of the string s. This is because we need to store the dp table, which is of size n x n.

Expand Around Center

The expand around center solution has a space complexity of O(1), as we are only storing a few variables to keep track of the longest palindrome.

Edge Cases

  1. Empty string: If the input string s is empty, the longest palindromic substring is an empty string.
  2. Single character string: If the input string s contains only one character, that character is the longest palindromic substring.
  3. String with no palindromic substring: If the input string s does not contain any palindromic substring (e.g., "abcde"), the longest palindromic substring is a single character from the string.
  4. Long string: Consider cases where the string is very long (up to 1000 characters as specified). The dynamic programming approach might use more space, but the expand around center remains efficient in terms of space.