Likou brushing notes - dynamic programming

 Dynamic Programming Basics

 DP for short , if a problem has many overlapping sub-problems , it is most effective to use dynamic programming.

 Each state in dynamic programming must be derived from the previous state

Question making process:

1. Determine the meaning of the dp array ( dp table ) and the subscript
2. Determine the recursive formula
3. How to initialize the dp array
4. Determine the traversal order
5. Example derivation dp array

509. Fibonacci numbers

509. Fibonacci Numbers - LeetCode https://leetcode.cn/problems/fibonacci-number/

 First think about the meaning of the dp array:

dp[i] refers to the value of the i-th number in the array

State transition equation: dp[i]=dp[i-1]+dp[i-2]

Initialization: the initial 1st and 2nd numbers

Determine the traversal order from the beginning to the nth

Example derivation dp[i] = dp[i - 1] + dp[i - 2]
class Solution {
public:
 int fib(int N) {
     if (N <= 1) return N;
     vector<int> dp(N + 1);
     dp[0] = 0;
     dp[1] = 1;
     for (int i = 2; i <= N; i++) {
     dp[i] = dp[i - 1] + dp[i - 2];
     }
     return dp[N];
}
};

70. Climb the stairs 

70. Climbing Stairs - LeetCode https://leetcode.cn/problems/climbing-stairs/

First think about the meaning of the dp array:

dp[i] means that there are several ways to climb to the i-th level

dp[i]=dp[i-1] + dp[i-2]

Initialization: the initial 1st and 2nd numbers

Determine the traversal order from the beginning to the nth

Example derivation dp[i] = dp[i - 1] + dp[i - 2]
class Solution {
public:
    int climbStairs(int n) {

        if(n<=2){
            return n;   
        }
        vector<int> vec(n+1,0);
        vec[1]=1;
        vec[2]=2;
        for(int i=3;i<=n;i++){
            vec[i]=vec[i-1]+vec[i-2];
        }        

        return vec[n];
    }
};

746. Climb Stairs with Minimum Cost


746. Climbing Stairs with Minimum Cost - LeetCode https://leetcode.cn/problems/min-cost-climbing-stairs/submissions/

dp[i] represents the minimum cost to reach the i-th step

dp[i]=min(dp[i-1],dp[i-2])+cost[i];

Initialize dp[0]=cost[0] dp[1]=cost[1]

The order starts  from the second step

class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int> dp(cost.size(),0);    
        //初始化数组
        dp[0]=cost[0];
        dp[1]=cost[1];
        for(int i=2;i<cost.size();i++){
            dp[i]=min(dp[i-1],dp[i-2])+cost[i];
        }
        return min(dp[cost.size()-1],dp[cost.size()-2]);
    }
};

 62. Different paths

62. Different Paths - LeetCode https://leetcode.cn/problems/unique-paths/

This question needs to record the number of paths that can be reached for each location

dp[i][j] represents the path to the point at row i, column j

dp[i][j]=dp[i-1][j]+dp[i][j-1] Each position can be obtained by moving forward from the upper and left positions

Initialization obviously dp[i][0]=1 dp[0][j]=1 means that the arrival paths of the first row and the first column are both 1

The order of traversal traverses from each layer layer by layer

class Solution {
public:
int uniquePaths(int m, int n) {
	vector<vector<int>>  dp(m, vector<int>(n, 1));
    
	for (int i = 1; i < m; i++) {
		for (int j = 1; j < n; j++) {
			dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
		}
	}

	return dp[m - 1][n - 1];
}
};

 63. Different Paths II

63. Unique Paths II - LeetCode https://leetcode.cn/problems/unique-paths-ii/

 Compared with the previous question, the ontology has more roadblocks in the middle

When initializing the map, we need to identify the obstacles

For each point, you can come through the upper or left node

State transition equation: grid[i][j]=grid[i-1][j]+grid[i][j-1]

Initialization: each element in the first row and first column is initialized to 1

Sequence: Traversing each line continuously downwards

Finally return grid[obstacledGrid.size()][obstacleGrid[0].size()]

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int n=obstacleGrid.size();
        int m=obstacleGrid[0].size();
        vector<vector<int>>  grid(n,vector<int>(m,0));
        //初始化数组 第一行和第一列可以到达的路径都是1
        for(int i=0;i<n&&obstacleGrid[i][0]==0;i++) grid[i][0]=1;
        for(int j=0;j<m&&obstacleGrid[0][j]==0;j++) grid[0][j]=1;

        for(int i=1;i<n;i++){
            for(int j=1;j<m;j++){
                if(obstacleGrid[i][j]==1) continue;
                grid[i][j]=grid[i-1][j]+grid[i][j-1];
            }
        }


        return grid[n-1][m-1];
    }
};

 343 integer split

343. Integer Break - LeetCode https://leetcode.cn/problems/integer-break/

dp[i] represents the largest product of i split

Recursion formula (state transition formula): traverse the previous dp data dp[i]=max(dp[i],max(dp[ij]*j,j*(ij)));

dp[i] is very small (acting as an iteration), so the maximum value is basically the value in the latter max (dp[ij] represents the maximum product that ij is split into, if the tree is multiplied by j, then Continuously loop, until the case of i-1, you can find out the maximum value of i split)

Initialization, for 0 and 1 cannot be split, then the largest product is 0

Sequence: starting from 1, from front to back

class Solution {
public:
    int integerBreak(int n) {
        vector<int> dp(n+1,0);
        dp[2]=1;
        for(int i=3;i<=n;i++){
            for(int j=1;j<i-1;j++){
                dp[i]=max(dp[i],max(dp[i-j]*j,j*(i-j)));
            }
        }
        return dp[n];
    }
};

96. Different Binary Search Trees 

96. Different Binary Search Trees - LeetCode https://leetcode.cn/problems/unique-binary-search-trees/

dp[n] One-dimensional array, thinking about meaning i means the number of binary search trees that i elements can form

State transition: dp[i]+=dp[j-1][ij] (j ranges from 1 to i)

Initially consider the case where i is 0: a search tree composed of 0 elements can be directly regarded as a binary search tree dp[0]=1

Order: from the first node until the nth node

return dp[n];

 Why is it multiplication, because the left and right subtrees are equivalent to different selection steps

Therefore, the number of final results should be the product of the two

class Solution {
public:
    int numTrees(int n) {
        
        //dp[i]为i个节点时,二叉搜索树的中枢
        vector<int> dp(n+1);
        dp[0]=1;
        
        for(int i=1;i<=n;i++){
            for(int j=1;j<=i;j++){
                dp[i]+=dp[j-1]*dp[i-j];
            }
        }

        return dp[n];
    }

};

knapsack problem

The knapsack problem is a classic problem of dynamic programming, including 01 knapsack, complete knapsack, multiple knapsack and other sub-problems

0-1 knapsack problem

There are N items and a knapsack that can be weighed up to W. The weight of the i- th item is weight[i] , and the resulting value is
value[i] . Each item can only be used once , and find out which items are put into the backpack with the largest total value.
To sum up, it is to put the given items into the backpack and find the maximum value that can be put into it.

 Define an array int dp[i][j], i is the same as the number of items and j is the maximum capacity of the backpack

dp[i][j] is the maximum value that can be obtained by putting the first i items in a reasonable capacity when the capacity is j

转移公式: dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);

Initialization: For j, j<value[0] is 0, if j>value[0], it is value[0];

int main(){
    vector<vector<int>> dp(weight.size()+1,vector<int>(rongliang+1,0));
    //全部初始化为0
    for(int j=value[0];j<=rongliang;j++){
        dp[0][j]=value[0];
    }
    for(int i=1;i<weight.size();i++){
        for(int j=0;j<=rongliang;j++){
            if(j<weight[i]) dp[i][j]=dp[i-1][j];
            else
            dp[i][j] = max(dp[i - 1][j], dp[i - 1][j-weight[i]] + value[i]);    
        }
    }

    cout << dp[weight.size() - 1][bagWeight] << endl;
}
#include<iostream>
#include<vector>

using namespace std;

int main() {
	int sum, rongliang;
	cin >> sum >> rongliang;
	vector<int>  tiji(sum+1, 0);
	vector<int>  jiazhi(sum+1, 0);
	for (int i = 1; i <= sum; i++) {
		cin >> tiji[i] >> jiazhi[i];
	}

	//初始化dp数组
	vector<vector<int>> dp(sum + 1, vector<int>(rongliang + 1, 0));

	//进行动态规划
	for (int i = 1; i <= sum; i++) {
		for (int j = rongliang; j >= 0; j--) {
			//状态转移方程
			if (j >= tiji[i]) {
				//装得下
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - tiji[i]] + jiazhi[i]);
			}
			else {
				//装不下
				dp[i][j] = dp[i - 1][j];
			}
		}
	}
	cout << dp[sum][rongliang];
	return 0;
}

These two codes are different:

First: the start positions of the value and weight arrays are different

Second: In the second round of the loop, the second program goes from big to small

The starting position is different, resulting in a different initialization process, the second one does not need to be initialized, and the initialization process is included in the loop

Because the value of dp is transferred from the state of the previous line, then we don’t care about the order of traversal, so it is the same from the beginning to the end, and from the end to the beginning, we can still start from the capacity and continue to decrease

Think about how to simplify the solution to the problem

Using a bit dynamic array can solve the problem

dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);

If the layer of dp[i-1] is copied to the i layer, the expression can be dp[i][j]=max(dp[i][j],dp[i][j-weight[ i]]+value[i]);

Just use a 1D array and just use dp[j]

416. Divide Equal Sum Subsets

416. Partition Equal Subset Sum - LeetCode https://leetcode.cn/problems/partition-equal-subset-sum/

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        
        int sum=0;
        vector<int> dp(10001,0);
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
        }
        if(sum%2!=0) return false;
        int target=sum/2;
        
        for(int i=0;i<nums.size();i++){
            for(int j=target;j>=nums[i];j--){
                dp[j]=max(dp[j],dp[j-nums[i]]+nums[i]);
            }
        } 


        if(dp[target]==target) return true; 
        return false;
    }
};

 Split the subset and find the same subset as

Divide in half to find the one with the largest capacity;

The meaning of the dp array, when the capacity is i, get the maximum value of the sum of array elements

dp[i]=max(dp[i],dp[i-nums[i]]+nums[i])

Initialization, when the capacity is less than the smallest array element, dp is zero, so the initialization is all zeros

1049. The Weight of the Last Stone II

1049. The Weight of the Last Stone II - LeetCode https://leetcode.cn/problems/last-stone-weight-ii/

class Solution {
public:
    int lastStoneWeightII(vector<int>& stones) {
        int sum=0;
    
        for(int i=0;i<stones.size();i++){
            sum+=stones[i];
        }
        vector<int> vec(150001,0);
        int target=sum/2;

        for(int i=0;i<stones.size();i++){
            for(int j=target;j>=stones[i];j--){
                vec[j]=max(vec[j],vec[j-stones[i]]+stones[i]);
            }
        }

        return sum-vec[target]-vec[target];
    }
};

Think about this question:
         the total number is divided into half, and the possibility of obtaining the maximum capacity

        sum-dp[target]-dp[target];

        sum-dp[target] must be greater than dp[target] 

494. Target and 

494. Target Sum - LeetCode https://leetcode.cn/problems/target-sum/

Add a +- sign in front of the number to get the number of the target sum

It is to hold enough things of a specific capacity. There are several ways to install them in total.

dp[j]+=dp[j-nums[i]] 

The order of traversal is the same as the 01 knapsack problem

Initialization: Through the state transition equation, it can be concluded that when it is 0, it means that there are several options when the result is 0

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {

        int sum=0;
        for(auto i:nums){
            sum+=i;
        }
        if(target>sum) return 0;
        if((target+sum)%2==1) return 0;

        int nice=(target+sum)/2;

        vector<int> vec(1001,0);
        vec[0]=1;
        for(int i=0;i<nums.size();i++){
            for(int j=nice;j>=nums[i];j--){
                vec[j]+=vec[j-nums[i]];
            }
        }
        return vec[nice];
    }
};

322 change change 

322. Change Exchange - LeetCode https://leetcode.cn/problems/coin-change/

The meaning of dynamic programming array: dp[i] represents the minimum number of coins required to get the total amount of i

dp[i] is the smallest of all dp[i - coins[j]] + 1

Recursion formula: dp[i]=min(dp[i-coins[j]]+1,dp[i])

How to initialize the array: the number of coins whose total amount is 0 must be 0, then dp[0]=0

Other dp[i] must be initialized to the maximum value

Determine the order of traversal: find the number of combinations, the size of the inner traversal backpack

                         Find the number of permutations, the outer layer traverses the size of the backpack

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        //设置dp数组
        vector<int> dp(amount+1,INT_MAX);

        //初始化dp数组
        dp[0]=0;

        //遍历背包
        for(int i=0;i<=amount;i++){
            for(int j=0;j<coins.size();j++){
                if(i>=coins[j] && dp[i-coins[j]]!=INT_MAX)
                    dp[i]=min(dp[i-coins[j]]+1,dp[i]);
            }
        }
        if (dp[amount] == INT_MAX) return -1;
        return dp[amount];
    }
};

139 word split 

string split

139. Word Breaking - LeetCode (LeetCode) https://leetcode.cn/problems/word-break/backtracked code:

Word backtracking is split and searched, if it can be found, it will return true, if all the results cannot be found, it will return false

 Direct violent backtracking, the recursion will time out, you need to use the memory array to record the previous results

 The ac code is as follows:

class Solution {
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(),wordDict.end());      
        vector<int> memory(s.size()+1,-1);
        return backtrace(s,wordSet,memory,0);
    }

    bool backtrace(string s,unordered_set<string>& wordDict,vector<int>& memory,int startindex){
        if(startindex>=s.size()){
            return true;
        }
        if(memory[startindex]!=-1) return memory[startindex]; 
        //如果已经被拆分过了就是用拆分过的值
        for(int i=startindex;i<s.size();i++){
            string word=s.substr(startindex,i-startindex+1);
            if(wordDict.find(word)!=wordDict.end() && backtrace(s,wordDict,memory,i+1)){
                memory[startindex]=1;// 记录以startIndex开始的⼦串是可以被拆分的
                return true;
            }
        }
        memory[startindex]=0; // 记录以startIndex开始的⼦串是不可以被拆分的
        //为什么不能再次拆分,因为之前的代码已经将这个起始位置后面的字符串进行了分割
        return false;
    }
};

The dynamic programming solution:

dp[i]: If the string length is i, dp[i] is true, indicating that one or more words that appear in the dictionary can be split

The recursive formula is if ( the substring of [j, i] appears in the dictionary && dp[j] is true) then dp[i] = true
Initialization of dp array, dp[0] is true, dp[0] is the basis of recursion, dp[0] is true
Traversal order: Combination or permutation is fine, it is best to choose the size of the outer recursive backpack and the inner loop items
class Solution {
public:

    bool wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordSet(wordDict.begin(),wordDict.end());      
        vector<bool> dp(s.size()+1,false);
        dp[0]=true;


        for(int i=1;i<=s.size();i++){
            for(int j=0;j<i;j++){
                string word=s.substr(,i-j);
                if (wordSet.find(word) != wordSet.end() && dp[j]) {
                    dp[i] = true;
                }
            }
        }

        return dp[s.size()];
    }
};

198 Robbery

198. House Robbery - LeetCode https://leetcode.cn/problems/house-robber/

The family knot problem is a classic dynamic programming:

dp[i] represents the maximum amount that the previous i family can steal

State transition equation: dp[i] can be deduced from dp[i-1] and dp[i-2]. If you want to steal the i-th house, you can’t steal the i-1th house, or don’t steal the i-th house

因此dp[i]=max(dp[i-1],dp[i-2]+nums[i])

Initialization: dp[2] needs to use dp[0] and dp[1] at the beginning. Therefore, initialize dp[0] to nums[0], dp[1]=max(nums[0],nums[1])

Traversal order: front to back

 ac's code:

class Solution {
public:
    int rob(vector<int>& nums) {
        
        vector<int> dp(nums.size(),0);

        if(nums.size()==0) return 0;
        if(nums.size()==1) return nums[0];

        dp[0]=nums[0];
        dp[1]=max(nums[0],nums[1]);

        for(int i=2;i<nums.size();i++){
            dp[i]=max(dp[i-2]+nums[i],dp[i-1]);
        } 

        return dp[nums.size()-1];
    }
};

121 Buying and selling stocks (buy and sell once) 

121. The best time to buy and sell stocks - LeetCode https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/

A classic dynamic programming problem:

It can be solved greedily, we need to find out the minimum cost and the maximum selling price, and ensure that the buying time is before selling

int min=1e9;

    int profit=0;

    for(int i=0;i<prices.size();i++){

        if(prices[i]<min){

            min=prices[i];

        }

        profit=prices[i]-min>profit?prices[i]-min:profit;

    }

    return profit;

 The practice of dynamic programming:

Dynamic programming table: dp[i][0] the cash in hand when i holds it, dp[i][1] the largest cash when it is sold on i day

State transition equation:

dp[i][0]=max(dp[i-1][0],-prices[i])

dp[i][1]=max(dp[i-1][1],prices[i]+dp[i-1][0]);

Initialization: From the state transition equation, it can be seen that dp[0][0] and dp[0][1] need to be initialized

Traversal order: front to back

 if(prices.size()==1) return 0;
    vector<vector<int>> dp(prices.size(),vector<int>(2,0));
    dp[0][0]=0-prices[0];
    dp[0][1]=0;
    for(int i=1;i<prices.size();i++){
        dp[i][0]=max(dp[i-1][0],0-prices[i]);
        dp[i][1]=max(dp[i-1][1],prices[i]+dp[i-1][0]);
    }   
    return dp[prices.size()-1][1];

 122 Buying and selling stocks (buying and selling multiple times)

122. The Best Time to Buy and Sell Stock II - LeetCode https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/

Compared with the previous question, this question can be bought and sold multiple times:

 dp[i][0] and dp[i][1] have the same meaning as before

State transition equation:
dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);

dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);

The key lies in the transformation of the state transition equation

The order of traversal is from front to back

ac code:

class Solution {
public:
    int maxProfit(vector<int>& prices) {

        vector<vector<int>> dp(prices.size(),vector<int>(2,0));
        if(prices.size()==1) return 0;
        dp[0][0]=0-prices[0];
        dp[0][1]=0;

        for(int i=1;i<prices.size();i++){
            dp[i][0]=max(dp[i-1][0],dp[i-1][1]-prices[i]);
            //可能在上一次将股票卖出之后还是会存在一些现金在手中
            dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);
        }

        return dp[prices.size()-1][1];
    }
};

 300 longest increasing subsequence

300. The longest increasing subsequence - LeetCode https://leetcode.cn/problems/longest-increasing-subsequence/

The key to this question is that dp[i] can be derived through dp[j] (j<i)
dp[i]=max(dp[i],(nums[j]<nums[i]?dp[j]+1:dp[j]); j belongs to [0-i-1],dp[i] The meaning is the longest incremental subsequence that can be formed using the first i elements. Initialization, when there is only one element, the size of the incremental subsequence is 1, and the order of traversal is from front to back
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        if(nums.size()==1) return nums.size();
        vector<int> dp(nums.size(),1);
        int max_num=0;
        for(int i=1;i<nums.size();i++){
            for(int j=0;j<i;j++){
                if(nums[i]>nums[j]) dp[i]=max(dp[j]+1,dp[i]);
                max_num=max(max_num,dp[i]);
            }
        }
        return max_num;
    }
};

 674 Longest Continuous Increasing Subsequence

674. Longest Continuous Increasing Subsequence - LeetCode https://leetcode.cn/problems/longest-continuous-increasing-subsequence/

The main idea of ​​this question is to need continuous increment

dp[i] represents the length of the longest incremental subsequence that can be formed by the first i elements

dp[i]=max(nums[i]>nums[i-1]?dp[i-1]+1:0,dp[i]);

The order of traversal is from forward to

Initialize ditto

class Solution {
public:
    int findLengthOfLCIS(vector<int>& nums) {
        vector<int> dp(nums.size(),1);
        if(nums.size()==1) return nums.size();
        int max_num=0;
        for(int i=1;i<nums.size();i++){
            if(nums[i]>nums[i-1]) dp[i]=max(dp[i-1]+1,dp[i]);
            max_num=max(max_num,dp[i]);
        }
        return max_num;
    }
};

718 Longest Repeating Subarray 

718. Maximum Length of Repeated Subarray - LeetCode https://leetcode.cn/problems/maximum-length-of-repeated-subarray/

The state of dp[i][j] of this question can be introduced by dp[i-1][j-1]

Initialization: dp[0][0] represents the value of the longest common subsequence whose end array subscript is -1, -1, dp[i-1][0],dp[0][j-1]

There is no special meaning, dp[0][0] is initialized to 0 for better derivation, dp[i-1][0], dp[0][j-1] are also initialized to 0

The result is traversed from front to back

class Solution {
public:
    int findLength(vector<int>& nums1, vector<int>& nums2) {
        vector<vector<int>> dp(nums1.size()+1,vector<int>(nums2.size()+1,0));
        int max_num=0;
        for(int i=1;i<=nums1.size();i++){
            for(int j=1;j<=nums2.size();j++){
                if(nums1[i-1]==nums2[j-1]) dp[i][j]=dp[i-1][j-1]+1;
                max_num=max(max_num,dp[i][j]);
            }
        }

        return max_num;
    }
};

Find the longest common substring of two strings

Dynamic programming problem:
The meaning represented by dp[i][j] is the length of the largest common substring among the first i characters of s1 and the first j characters of s2

State transition equation: if(s1[i],s2[j])dp[i][j]=dp[i-1][j-1]+1

Recursive order: Exchange the contents of the two strings at the beginning, traversing from front to back

#include<iostream>
#include<vector>

using namespace std;

int main() {
	string s1, s2;
	cin >> s1 >> s2;
	if(s1.size()>s2.size())
	swap(s1, s2);

	int len1 = s1.size();
	int len2 = s2.size();

	vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));
	int start = 0;
	int max = 0;
	for (int i = 1; i <= len1; i++) {
		for (int j = 1; j <= len2; j++) {
			if (s1[i - 1] == s2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
			if (dp[i][j] >= max) {
				max = dp[i][j];
				start = i - max;
			}


		}
	}


	cout << s1.substr(start, max);
}

1143 longest common subsequence

1143. Longest Common Subsequence - LeetCode icon-default.png?t=N5K3https://leetcode.cn/problems/longest-common-subsequence/

dp[i][j] represents the longest common subsequence among the first i characters of string1 and the first j characters of string2

State transition formula: if string[i-1]==string2[j-1] then dp[i][j]=dp[i-1][j-1]+1

                         If not the same, then dp[i][j] takes the maximum value of dp[i,j-1] and dp[i-1][j]

Order of recursion: from front to back, two loops

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        if(text1.size()>text2.size()){
            swap(text1,text2);
        }
        int max_flag=0;
        vector<vector<int>> dp(text1.size()+1,vector<int>(text2.size()+1,0));
        for(int i=1;i<=text1.size();i++){
            for(int j=1;j<=text2.size();j++){
                if(text1[i-1]==text2[j-1]){
                     dp[i][j]=dp[i-1][j-1]+1; 
                }else
                {
                    dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
                }
            }
        }

        return dp[text1.size()][text2.size()];
    }
};

Guess you like

Origin blog.csdn.net/qq_53633989/article/details/130745610