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]