class Solution:
    def longestPalindromeSubseq(self, s: str) -> int:
        # time O(N^2)  space O(N^2)
        #dp[i][j] stands for longest palindrom from s[i:j+1] 

        # step1 initialize 
        size = len(s)
        # why other value assigned 0 ?  for s[i:j] where i > j is impossible so set to 0 
        dp = [[0 for _ in range(size)] for _ in range(size)]
        # step2 base case: mininum palindrome is char itself with length 1
        for i in range(size):
            dp[i][i] = 1
        # step3: loop through right bottom corner ascending order, start after dp[i][i]
        for i in range(size-1, -1, -1):
        # step3.1: given s[i+i][j-1], get d[i][j]
            for j in range(i+1,size):
                #case 1 s[i] == s[j]
                if s[i] == s[j]:
                    dp[i][j] = dp[i+1][j-1]+2
                # case 2  s[i] != s[j], means either s[i] or s[j] will be included to get longest pa subseq from s[i:j+1]
                else:
                    dp[i][j] = max(dp[i+1][j],dp[i][j-1])
        # step4: return final result 
        return dp[0][size-1]