Dynamic programming -- palindrome string series

Posted by oscardog on Sat, 05 Feb 2022 10:48:02 +0100

1. Longest palindrome substring

2. 

 

preface

Dynamic programming palindrome string series has always been a pain point for me. After reading the book this morning, I thought I had fully understood it. As a result, I closed the book and knocked the code in the afternoon. I haven't finished it all afternoon. Alas, sure enough, the code doesn't depend on reading. The process of thinking in the middle is difficult. I can feel it only after knocking the code

Train of thought

dp Trilogy:

1) Determining dp[i][j] meaning is the most important and difficult step. Why is it the most difficult step? Does DP [i] [J] mean a string with subscript [i,j] or a string with subscript [i+1][j-1]? Or a string with subscript [i-1][j-1]? We choose the string meaning of the subscript [i] [J] because it is convenient for subsequent processing

2) Recurrence formula:

Question 1:

if(s[i]==s[j]) dp[i][j]=dp[i+1][j-1]+2, otherwise dp[i][j]=max(dp[i+1][j],dp[i][j+1]) / / indicates that a longer sequence is selected

Question 2:

if(s[i]==s[j]) three cases are discussed:

                        ①.i==j

                        ②.j-i==1 ,eg:aa

                        ③. J-I > 1 , we need to judge whether dp[i][j] is a palindrome string by judging dp[i+1][j-1]

3) Initialize

Question 1:

For the elements except dp[i][i] on the diagonal, the initial value is 1, indicating that we calculate a length from ourselves to ourselves, and the remaining length is 0

Question 2:

We initialize the diagonal to true and the rest to false

4) Traversal order:

What is worth mentioning about these two questions is the traversal order: from bottom to top and from left to right, because our judgment of dp[i][j] depends on dp[i+1][j-1]

5) Key points:

Yes! The traversal order is determined, but how to ensure that the cumbersome + 1 and - 1 operations will not overflow?

Many people are confused and think it's over, but he may not be able to answer when asked about the boundary handling logic

First question:

Because j > = I, we just need to fill the upper right corner. In order to prevent [i+1][j-1] from crossing the boundary, we actually prevent [0] [0] and [size-1][size-1] from crossing the boundary. So how to prevent it? We have initialized the elements on [i][i] in advance, so we don't need to set j to I, but to i+1, so we can skillfully avoid those two positions where the subscript crosses the boundary

Second question:

This question is special because of the classification discussion (why should I classify and discuss the case of s[i]==s[j]? It's very simple because they are the same, adjacent and separated by at least one. In the first three cases, the first two cases are the same, and the third case needs to judge its lower left element [i]+
1] After [J-1], dp[i][j] can be assigned to true. For example, abca, s[0]==s[3], but dp[1][2] is false, so dp[0][3] cannot be set to true (we initialize to false, so we don't need to operate)), so when J-I < = 1, dp[i][j]=true,res + +; This situation must be legal, instead of dp[i+1][j-1] (this situation is only managed when J-I > 1, and the subscript of a matrix will cross the boundary only in [0] [0] and [size-1] [size-1])

First code:

#include<string>
#include<vector>
using namespace std;
#include<string>
#include<vector>
using namespace std;

class Solution {
public:
    int longestPalindromeSubseq(string s) {
        int size = s.size();
        vector<vector<int>> dp(size , vector<int>(size , 0));
        for (int i = 0; i < size; ++i)    dp[i][i] = 1;
        for (int i = size - 1; i >= 0; --i) {
            for (int j = i + 1; j < size; ++j) {      //If j=i+1 is set here, the boundary problem does not need to be considered
                if (s[i ] == s[j]) {
                    dp[i][j] = dp[i + 1][j - 1] + 2;
                }
                else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
            }
        }
        return dp[0][size - 1];
    }
};

Second code:

class Solution {
public:
    int countSubstrings(string s) {
        //dp[i][j] indicates whether [i,j] is a palindrome string
        /*
        * if(s[i]==s[j])
        * There are three situations:
        *   ①.i==j
        *   ②.j-i==1
        *   ③.j-i>1
        * 
        */
        int size = s.size();
        int res = 0;
        vector<vector<bool>> dp(size,vector<bool>(size, false));
        for (int i = 0; i < size; ++i)  dp[i][i] = true;
        for (int i = size-1; i >= 0; --i) {
            for (int j = i; j < size; ++j) {
                if (s[i] == s[j]) {
                    if (j - i <= 1) { dp[i][j] = true; res++; }
                    else if (dp[i + 1][j - 1]) {
                        dp[i][j] = true;
                        res++;
                    }
                }
            }
        }
        return res;
    }
};

Topics: Algorithm Dynamic Programming