pythonmemorypalindrome

Leetcode memory optimisation - Longest Palindromic Substring


I've done a lot of Leetcode questions lately. When I failed to do some question, I've always knew why, but not this time - question 5. Longest Palindromic Substring:

Given a string s, return the longest palindromic substring in s.

Example 1:

Input: s = "babad"
Output: "bab"

Explanation: "aba" is also a valid answer.

Example 2:

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

My code:

class Solution:
    def longestPalindrome(self, s: str) -> str:
        def is_palindrom(word):
            for i,letter in enumerate(word):
                if letter != word[-i-1]:
                    return False
            return True
        out = ""
        checked_words = set()
        def dp(word):
            nonlocal out
            if len(word)>len(out) and word not in checked_words:
                checked_words.add(word)
                if is_palindrom(word) and len(word)>len(out):
                    out = word
                dp(word[:-1])
                dp(word[1:])
        dp(s)
        return out

Issue: Memory Limit Exceeded on imput: s = "rgczcpratwyqxaszbuwwcadruayhasynuxnakpmsyhxzlnxmdtsqqlmwnbxvmgvllafrpmlfuqpbhjddmhmbcgmlyeypkfpreddyencsdmgxysctpubvgeedhurvizgqxclhpfrvxggrowaynrtuwvvvwnqlowdihtrdzjffrgoeqivnprdnpvfjuhycpfydjcpfcnkpyujljiesmuxhtizzvwhvpqylvcirwqsmpptyhcqybstsfgjadicwzycswwmpluvzqdvnhkcofptqrzgjqtbvbdxylrylinspncrkxclykccbwridpqckstxdjawvziucrswpsfmisqiozworibeycuarcidbljslwbalcemgymnsxfziattdylrulwrybzztoxhevsdnvvljfzzrgcmagshucoalfiuapgzpqgjjgqsmcvtdsvehewrvtkeqwgmatqdpwlayjcxcavjmgpdyklrjcqvxjqbjucfubgmgpkfdxznkhcejscymuildfnuxwmuklntnyycdcscioimenaeohgpbcpogyifcsatfxeslstkjclauqmywacizyapxlgtcchlxkvygzeucwalhvhbwkvbceqajstxzzppcxoanhyfkgwaelsfdeeviqogjpresnoacegfeejyychabkhszcokdxpaqrprwfdahjqkfptwpeykgumyemgkccynxuvbdpjlrbgqtcqulxodurugofuwzudnhgxdrbbxtrvdnlodyhsifvyspejenpdckevzqrexplpcqtwtxlimfrsjumiygqeemhihcxyngsemcolrnlyhqlbqbcestadoxtrdvcgucntjnfavylip"

I've tested my solution on my local setup and solution don't require a lot of RAM memory - about 128MB (tested with process.memory_info().rss), 16MB just for checked_words set (tested with sys.getsizeof()). What could be an issue? How to identify which part of program is causing memory problems?


Solution

  • Your code is too inefficient; there is no need to run a linear loop for every single possible substring to check if it is a palindrome. Since the maximum string length for this problem is only 1000, we can use a simple O(|s|^2) dynamic programming solution that makes use of previous computed results.

    Let is_palindrome[l][r] indicate whether the substring from index l to r (inclusive) is a palindrome. First, we initialize this table for all length 1 and 2 palindromes. For substrings of length at least 3 (r - l > 2), is_palindrome[l][r] will be true iff if the characters at index l and r match and the substring formed by cutting off one character from each end is also a palindrome (i.e. is_palindrome[i + 1][r - 1] is true).

    class Solution:
        def longestPalindrome(self, s: str) -> str:
            is_palindrome = [[False] * len(s) for _ in s]
            ans = (0,1)
            for i in range(len(s)): is_palindrome[i][i] = True
            for i in range(len(s) - 1): 
                if s[i] == s[i + 1]:
                    is_palindrome[i][i + 1] = True
                    ans = (i,i+2)
            for l in range(3, len(s) + 1):
                for i in range(0, len(s) - l + 1):
                    if s[i] == s[i + l - 1] and is_palindrome[i + 1][i + l - 2]:
                        is_palindrome[i][i + l - 1] = True
                        ans = (i,i+l)
            return s[slice(*ans)]
    

    Note that there are also solutions that solve this problem in O(|s|) time, but that is not necessary for this question.