LeetCode132. Segmented palindrome string II - string dynamic programming

Posted by fred_belanger on Sun, 30 Jan 2022 23:46:04 +0100

Topic overview


Title Link: Ask me to do the problem

Problem solution

1, General dynamic programming

   in that sentence, the dynamic programming of string class can consider the state of the response problem of the first i characters, such as this problem and definition f ( i ) f(i) f(i) is the minimum number of segments of the first i characters of string s into palindrome substrings. Assuming that the length of string s is n, the answer to the question is f ( n ) f(n) f(n).
   consider the state transition equation: if i characters from the beginning to the end are palindrome strings at this time, there is no need to divide, and the number of divisions is 0; Otherwise, the problem can be decomposed in this way: separate the string from the j-th character( 0 < j < i 0<j<i 0 < J < i), if the characters j+1 to i form a palindrome string, a segmentation scheme is obtained: divide the first j characters into palindrome substrings, and then add the characters j+1 to j to form a palindrome substring. At this time, the segmentation number is equal to the minimum segmentation number of the first j characters into palindrome substrings f ( j ) f(j) f(j) plus the j-th character, i.e f ( j ) + 1 f(j)+1 f(j)+1. In order to get the minimum number of partitions, we need to traverse j from 1 to i-1 to select the minimum number of partitions that meet the conditions.
  as for how to judge palindrome strings, you can use the double finger needle method.
  the boundary conditions of this problem are obviously f ( 0 ) = 0 , f ( 1 ) = 0 f(0) = 0,f(1) = 0 f(0)=0,f(1)=0,0 characters do not need to be segmented, and 1 character does not need to be segmented. It is itself a palindrome string; In other cases, since we require the minimum value, one scheme is to initialize all to INT_MAX, another scheme is to find the interval containing i characters. The worst case is to divide i-1 into i single characters. They are palindrome strings, so the initial conditions can also be written as f ( i ) = i − 1 f(i)=i - 1 f(i)=i−1.
code:

class Solution {
public:
    int minCut(string s)
    {
        /*
        f(i)Considering the 1st character to the ith character, the minimum number of segments divided into palindrome substrings
        State transition equation:
        If the whole is a palindrome string, that is, when J is equal to 0, characters j+1=1 to i are palindrome strings
        Then f(i) is equal to 0
        Otherwise, J starts from 1. If the j+1 to i characters are palindrome strings
        Then the number of methods is equal to the number of methods f(j) after the j-th character and one knife 1 plus the first and last j characters
        That is, f (I) = f (J) + 1 J < I
        So do a search f (I) = min (check (f (J)) + 1
        */
        int size = s.size();
        if (check(s, 0, size - 1) == true)
        {
            return 0;
        }
        vector<int> dp(size + 1, INT_MAX);
        /*
         *Here, the initial state can also be made into dp[0] = 0, and other i dp[i] = i - 1
         *The initial maximum number of divisions is to divide them into one character
        */
        dp[0] = 0;
        dp[1] = 0;
        for (int i = 2; i <= size; ++i)
        {
            for (int j = 0; j < i; ++j)
            {
                if (check(s, j, i - 1) == true)
                {
                    if (j == 0)
                    {
                        /*Corresponding whole*/
                        dp[i] = 0;
                        break;
                    }
                    else
                    {
                        /*Corresponding to j=1 to i-1*/
                        dp[i] = min(dp[i], dp[j] + 1);
                    }
                }
            }
        }
        return dp[size];
    }
    bool check(const string& s, int left, int right)
    {
        if (left == right)
        {
            return true;
        }
        while (left < right)
        {
            if (s[left] != s[right])
            {
                return false;
            }
            ++left;
            --right;
        }
        return true;
    }
};

Time complexity: O ( n 3 ) O(n^3) O(n3)
Space complexity: O ( n ) O(n) O(n)

2, Memory search optimization

   this problem can be further optimized. We can do this operation in advance and store the state in a two-dimensional matrix s t a t e state In state, s t a t e [ i ] [ j ] state[i][j] state[i][j] indicates whether the substring with subscript i to subscript j is a palindrome string. For judging whether it is a palindrome string, we previously Three solutions to the longest palindrome substring A dynamic programming method is introduced, that is, F(i,j) is defined to indicate whether the substring with subscript i to subscript j is a palindrome substring, and the state transition equation is F ( i , j ) = ( s [ i ] = = s [ j ] ) & & F ( i + 1 , j − 1 ) F(i,j) = (s[i]==s[j])\&\&F(i+1,j-1) F (I, J) = (s [i] = = s [J]) & & F (I + 1, J − 1), the initial state is F ( i , i ) = t r u e , F ( i , i + 1 ) = ( s [ i ] = = s [ i + 1 ] ) F(i,i)=true,F(i,i+1) = (s[i]==s[i+1]) F(i,i)=true,F(i,i+1)=(s[i]==s[i+1]), which can be taken separately as one O ( n 2 ) O(n^2) The algorithm of O(n2) is put outside. Judge whether each substring is a palindrome substring first, and then go through the loop to judge directly [ j , i − 1 ] [j,i-1] Whether [j,i − 1] is a palindrome string depends directly s t a t e [ j ] [ i − 1 ] state[j][i-1] state[j][i − 1].
   for this state transition equation, i know two ways to write it. One is to let the outer loop i traverse from large to small, and the inner loop j traverse from i to N, which is the idea of traversing upward from the lower right corner of the diagonal; Another writing method is to control the length of the palindrome substring judged at this time and traverse from L=2 to L=n. because the relationship required to judge whether the string with length L is a palindrome string is whether the string with length L-2 is a palindrome string, so this traversal also ensures that the answer can be covered.
   codes for both implementations are given:
Traverse upward from the lower right corner of the diagonal:

class Solution {
public:
    int minCut(string s)
    {
        /*
        The algorithm just is an O(N^3) algorithm. In fact, it can segment whether (j,i-1) is a palindrome string in advance
        Put it outside and write it. As for judging whether it is a palindrome string, you can use dynamic programming
        f(i,j) = (s[i] == s[j]) && f(i + 1, j - 1)
        f(i,i) = true; f(i, i + 1) = (s[i] == s[i + 1])
        */
        int size = s.size();
        auto state = getstate(s);
        /*
        Follow the original dynamic programming F(i) = 0 if state(0, i - 1) == true
        else F(i) = min(F(i), F(j) + 1) j state(j,i - 1) == true
        */
        vector<int> dp(size + 1);
        for (int i = 1; i <= size; ++i)
        {
            /*The initial minimum number of divisions is the number of characters minus 1, that is, all are divided into one character*/
            dp[i] = i - 1;
        }
        for (int i = 2; i <= size; ++i)
        {
            for (int j = 0; j < i; ++j)
            {
                if (state[j][i - 1])
                {
                    if (j == 0)
                    {
                        dp[i] = 0;
                        break;
                    }
                    else
                    {
                        dp[i] = min(dp[i], dp[j] + 1);
                    }
                }
            }
        }
        return dp[size];
    }
    vector<vector<bool>> getstate(const string& s)
    {
        int n = s.size();
        vector<vector<bool>> ret(n, vector<bool>(n));
        for (int i = n - 1; i >= 0; --i)
        {
            for (int j = i; j < n; ++j)
            {
                if (i == j)
                {
                    ret[i][j] = true;
                }
                else if (j == i + 1)
                {
                    ret[i][j] = (s[i] == s[j]);
                }
                else
                {
                    ret[i][j] = (s[i] == s[j]) && ret[i + 1][j - 1];
                }
            }
        }
        return ret;
    }
};

Control the length of palindrome string judged in each cycle:

class Solution {
public:
    int minCut(string s)
    {
        /*
        The algorithm just is an O(N^3) algorithm. In fact, it can segment whether (j,i-1) is a palindrome string in advance
        Put it outside and write it. As for judging whether it is a palindrome string, you can use dynamic programming
        f(i,j) = (s[i] == s[j]) && f(i + 1, j - 1)
        f(i,i) = true; f(i, i + 1) = (s[i] == s[i + 1])
        */
        int size = s.size();
        auto state = getstate(s);
        /*
        Follow the original dynamic programming F(i) = 0 if state(0, i - 1) == true
        else F(i) = min(F(i), F(j) + 1) j state(j,i - 1) == true
        */
        vector<int> dp(size + 1);
        for (int i = 1; i <= size; ++i)
        {
            /*The initial minimum number of divisions is the number of characters minus 1, that is, all are divided into one character*/
            dp[i] = i - 1;
        }
        for (int i = 2; i <= size; ++i)
        {
            for (int j = 0; j < i; ++j)
            {
                if (state[j][i - 1])
                {
                    if (j == 0)
                    {
                        dp[i] = 0;
                        break;
                    }
                    else
                    {
                        dp[i] = min(dp[i], dp[j] + 1);
                    }
                }
            }
        }
        return dp[size];
    }
    vector<vector<bool>> getstate(const string& s)
    {
        int n = s.size();
        vector<vector<bool>> ret(n, vector<bool>(n));
        for (int i = 0; i < n; ++i)
        {
            ret[i][i] = true;
        }
        for (int L = 2; L <= n; ++L)
        {
            for (int i = 0; i < n; ++i)
            {
                int j = i + L - 1;
                if (j >= n)
                {
                    break;
                }
                if (L == 2)
                {
                    ret[i][j] = (s[i] == s[j]);
                }
                else
                {
                    ret[i][j] = (s[i] == s[j]) && ret[i + 1][j - 1];
                }
            }
        }
        return ret;
    }
};

Topics: C++ Algorithm leetcode string Dynamic Programming