Spring Festival Titles (5)

leetcode 55 jumping game && leetcode 45 jumping game II

55
Given one 非负整数数组nums, you are initially at the first subscript of the array, 数组中的每个元素代表你在该位置可以跳跃的最大长度;
judge whether you can reach the last subscript;

For example:
Input: nums = [2,3,1,1,4]
Output: true
Explanation: You can jump 1 step first, from subscript 0 to subscript 1, and then jump 3 steps from subscript 1 to the last subscript.

//贪心解法
//思路:
//i每次移动只能在cover的范围内移动,每移动一个元素,cover得到该元素数值(新的覆盖范围)的补充,让i继续移动下去。
//而cover每次只取 max(该元素数值补充后的范围, cover本身范围)。
//如果cover大于等于了终点下标,直接 return true 就可以了。
class Solution
{
    
    
public:
	bool canJump(vector<int>& nums)
	{
    
    
		int cover = 0;//初始化一个变量用来表示可以跳跃的覆盖范围
		if (nums.size() == 1)return true;
		for (int i = 0; i <= cover; i++)//在当前可以遍历到的覆盖范围内进行遍历; i每次移动只能在cover的范围内移动,每移动一个元素,cover得到该元素数值(新的覆盖范围)的补充,让i继续移动下去。
		{
    
    
			cover = max(i + nums[i], cover);//更新最大的覆盖范围
			if (cover >= nums.size() - 1)return true;//最终的问题转换为跳跃覆盖范围是不是可以覆盖到终点
		}
		return false;
	}
};

//暴力遍历寻找 --- 效率和上述解法相当
//对任意一个点而言 由此点可以到达的最远距离是 i+nums[i],这块距离表示从起始位置到最远位置的距离  当这个距离小于i时 肯定是到不了终点的;
//k始终维护一个可以由任意点到达的最远距离;
//如果任意一个点可以到达的最远距离还是到不了终点 始终会存在一个位置i 使得k<i,这个i会很靠后;
class Solution {
    
    
public:
    bool canJump(vector<int>& nums) 
    {
    
    
        int k = 0;
        for (int i = 0; i < nums.size(); i++) 
        {
    
    
            if (i > k) return false;
            k = max(k, i + nums[i]);
        }
        return true;
    }
};


45
Given one 非负整数数组nums, you are initially at the first position of the array;
each element in the array represents the maximum length you can jump at that position;
your goal is 使用最少的跳跃次数达到数组的最后一个位置;

Example:
Input: nums = [2,3,1,1,4]
Output: 2
Explanation: The minimum number of jumps to get to the last position is 2.
Jump from index 0 to index 1, jump 1 step, then jump 3 steps to reach the last position of the array.

Method 1 – Violent traversal efficiency is also good
insert image description here

Small note:

class Solution
{
    
    
public:
    int canJump(vector<int>& nums)
    {
    
    
        int ans = 0;//记录跳跃次数
        int start = 0;//第一次肯定要跳跃 跳跃位置就是第一个位置
        int end = 1;
        while (end < nums.size())//循环结束的标志是当 end的范围超过数组的边界 end>=nums.size() 就结束循环 此时ans就是最少的跳跃次数;
        {
    
    
            int max_pos=0;//一定要进行赋值初始化 不能只写int max_pos
            for (int i = start; i < end; i++)//[start,end]是一个起跳范围==在start和end的范围内进行遍历 不断的更新起始位置和终止位置 
            {
    
    
                max_pos = max(max_pos, i + nums[i]);
            }
            start = end;//更新下一次起跳的起始位置:前一次跳跃的结束位置就是下一次跳跃范围的起始位置;
            end = max_pos + 1;//更新下一次起跳的结束位置:前一次可以跳跃的最远距离+1 就是下一次跳跃范围的结束位置;
            ans++;
        }
        return ans;
    }
};

Method 2 - Code Random Record Solution - Algorithm Idea:
Core:移动下标只要遇到当前覆盖最远距离的下标,直接步数加一,不考虑是不是终点的情况 ;
To achieve this effect, just move the subscript 最大只能移动到nums.size - 2to the place.
Because when moving the subscript to nums.size - 2:

如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即ans++),因为最后一步一定是可以到的终点。(题目假设总是可以到达数组的最后一个位置),如图:

insert image description here

If 移动下标不等于当前覆盖最大距离下标, it means that the farthest distance currently covered can directly reach the end point, and there is no need to take another step. As shown in the picture:
insert image description here

// 版本二
class Solution {
    
    
public:
    int jump(vector<int>& nums) {
    
    
        int curDistance = 0;    // 当前覆盖的最远距离下标
        int ans = 0;            // 记录走的最大步数
        int nextDistance = 0;   // 下一步覆盖的最远距离下标
        for (int i = 0; i < nums.size() - 1; i++) {
    
     // 注意这里是小于nums.size() - 1,这是关键所在
            nextDistance = max(nums[i] + i, nextDistance); // 更新下一步覆盖的最远距离下标
            if (i == curDistance) {
    
                     // 遇到当前覆盖的最远距离下标
                curDistance = nextDistance;         // 更新当前覆盖的最远距离下标
                ans++;
            }
        }
        return ans;
    }
};





binary search

class Solution
{
    
    
public:
	int search(vector<int>& nums, int target)
	{
    
    
		int l = 0, r = nums.size() - 1;
		while (l <= r)
		{
    
    
			int mid = l + (r - l) / 2;
			if (nums[mid] == target)
			{
    
    
				return mid;
			}
			else if (nums[mid] < target)l = mid + 1;
			else if (nums[mid] > target)r = mid - 1;
		}
		return -1;
	}
};

The sword refers to the straight in offer 61 playing cards

Topic:
Draw randomly from several decks of playing cards 5 张牌to determine whether it is a straight, that is, whether the 5 cards are continuous.
2 to 10 are the numbers themselves, A is 1, J is 11, Q is 12, and K is 13 ,而大、小王为 0 ,可以看成任意数字. A cannot be considered as 14.

For example:
input 1 2 3 4 5
output true

Enter 0 0 1 2 5
and return true

accomplish:

//随机抽取五张牌 判断是不是顺子
//因为只有五张牌 要想组成顺子 最大值和最小值之间差值只能小于等于4,若小于4,可以用0进行替;
class Solution
{
    
    
public:
	bool isStraight(vector<int>& nums)
	{
    
    
		vector<int>map(14,0);//数组实现哈希集合 初始化一个数组
		int minValue = INT_MAX, maxValue = INT_MIN;//一开始将最大值初始化为最小值 将最小值初始化为最大值 防止边界溢出
		for (int num : nums)
		{
    
    
			if (map[num] >= 1)return false;//只有五张牌 若出现重复数字 肯定是不能组成顺子的
			if (num == 0)continue;//大小王不作任何处理 需要的时候直接进行替换即可
			minValue = min(minValue, num);//第一次比较的结果是num;不断的更新遍历到的最小值, 最终返回的是整个数组中的最小值
			maxValue = max(maxValue, num);//第一次比较的结果是num ;不断的更新遍历到的最大值,最终返回的是整个数组中的最大值
			map[num]++;//统计出现次数
		}
		return  maxValue - minValue <= 4;
	}
};

leetcode 739 daily temperature

Topic:
Given one 整数数组 temperatures, which represents the daily temperature, return one 数组 answer, where answer[i]means that after the i-th day, there will be a higher temperature. If the temperature doesn't rise after that, substitute 0 in this place.

For example:
input: temperatures = [73,74,75,71,69,72,76,73]
output:[1,1,4,2,1,1,0,0]

insert image description here
Core idea:下标差 the distance between the two is the number of days to return


class Solution
{
    
    
public:
	vector<int>dailyTemperatures(vector<int>& T)
	{
    
    
		int n = T.size();
		vector<int>ans(n);//初始化一个容器存储需要间隔的天数
		stack<int>s;//初始化一个辅助栈存储下标,进行栈顶元素和当前遍历到的元素的比较
		for (int i = 0; i < n; i++) //i是当前遍历到的元素的下标
		{
    
    
			while (!s.empty() && T[i] > T[s.top()])//栈中不为空并且当前遍历到的元素大于之前的元素;
			{
    
    
				auto t = s.top();//t代表前一个较小元素的下标
				s.pop();//将之前那个较小的元素从栈顶删除
				ans[t] = i - t;//将之前位置的元素更新为需要间隔的天数=当前遍历到那一天的下标-前一天的下标;ans[t]是前一天距离升温间隔的天数
			}
			s.push(i);//如果当前遍历到的元素比栈顶元素小 就一直入栈
		}
		return ans;
	}
};


int main()
{
    
    
	Solution so;
    vector<int>nums = {
    
     30,40,50,60};
	
	vector<int>res;
	res = so.dailyTemperatures(nums);

	for (vector<int>::iterator it=res.begin(); it!=res.end(); it++)//迭代器的遍历输出vector中所有元素
	{
    
    
		cout << "输出结果是;" << *it << endl;
	}
	
	system("pause");
	return 0;
}

leetcode 162 Finding Peaks

Given one 整数数组 nums, find the peak element and return its index. The array may contain multiple peaks, in which case it is sufficient to return the location of any one of the peaks.

Input: nums = [1,2,3,1]
Output: 2
Explanation: 3 is the peak element and your function should return its index 2.

insert image description here

Core ideas:二分查找

class Solution
{
    
    
public:
	int findPeakElement(vector<int>& nums)
	{
    
    
		if (nums.size() == 1)return  0;
		int l = 0, r = nums.size() - 1;
		while (l < r)
		{
    
    
			int mid = l + (r - l) / 2;
			if (nums[mid] > nums[mid + 1]) //下降 左侧必有峰
			{
    
    
				r = mid;//更新右边界
			}
			else if (nums[mid] < nums[mid + 1]) //如果上升 则右侧必有峰
			{
    
    
				l = mid + 1;//更新左边界
			}
		}
		return r;//return l 一样的结果
	}
};

leetcode 72 edit distance H

insert image description here
insert image description here


class Solution
{
    
    
public:
	int minDistance(string word1, string word2)
	{
    
    
		int m = word1.length();
		int n = word2.length();
		//dp[i][j]表示word1中的前i个字符转换成word2中的前j的字符 最少的操作次数;
		vector<vector<int>>dp(m + 1, vector<int>(n + 1));
		for (int i = 0; i <= m; i++)
		{
    
    
			dp[i][0] = i;//word2为空串的情况
		}
		for (int j = 0; j <= n; j++)
		{
    
    
			dp[0][j] = j;//word1为空串的情况
		}
		for (int i = 1; i <= m; i++)
		{
    
    
			for (int j = 1; j <= n; j++)
			{
    
    
				if (word1[i - 1] == word2[j - 1])
				{
    
    
					dp[i][j] = dp[i - 1][j - 1];//直接继承上一个状态即可
				}
				else
				{
    
    
					dp[i][j] =1+min(dp[i - 1][j - 1], min(dp[i][j - 1], dp[i - 1][j]));//三种情况取最小的即可
				}
			}
		}
		return dp[m][n];
	}
};


leetcode 1143 longest common subsequence

Given 两个字符串 text1 和 text2, return the longest 公共子序列length of these two strings . This subsequence may not be continuous, but the original order cannot be broken . Returns 0 if no common subsequence exists.
Example:
Input: text1 = "abcde", text2 = "ace"
Output: 3
Explanation: The longest common subsequence is "ace", which has length 3.

Algorithm idea:
dynamic programming

insert image description here
insert image description here

Small note: Indicates the length of the longest common subsequence when
dp[i][j]traversing to this position;(i,j)

Code:

//代码随想录 DP解法
class Solution
{
    
    
public:
	int longestCommonSubsequence(string text1, string text2)
	{
    
    
		vector<vector<int>>dp(text1.size() + 1, vector<int>(text2.size() + 1, 0));//初始化一个text1.size()+1行 text2.size()+1列的二维数组
		for (int i = 1; i <= text1.size(); i++)
		{
    
    
			for (int j = 1; j <= text2.size(); j++)
			{
    
    
				if (text1[i - 1] == text2[j - 1])//比较两字符串当前位置的元素是否相等 相等的情况下 从上一个状态+1转换而来
				{
    
    
					dp[i][j] = dp[i - 1][j - 1] + 1;
				}
				else //两字符串当前位置的元素不等的情况下 继承自上一个状态 取二者中最长子序列的较大值
				{
    
    
					dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
				}
			}
		}
		return dp[text1.size()][text2.size()];
	}   //dp[i][j]表示text1的[1,i]区间和text2的[1,j]区间的最长公共子序列的长度;
};


leetcode 718 longest repeating subarray

Given 两个整数数组nums1和nums2Returns the length of the longest common subarray in two arrays. The subarrays must be continuous ;
for example:
input: nums1 = [1,2,3,2,1], nums2 = [3,2,1,4,7]
output: 3
explanation: the longest common subarray is [3,2,1].

Dynamic programming solution:

insert image description here

//理解一下这俩题的区别:
//最长重复子数组和最长连续子序列的两个题的区别在于所找数组是不是可以连续
//对于连续的重复子数组 其末尾元素必须相等 若末尾不相等 则就没有比较的必要了 末尾元素不等 最长重复子数组的长度肯定为0
//对于重复子序列 因为要求可以不连续 即使末尾元素不等 还要继续向前比较 
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 res = 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;
					}   //这个题和最长连续子序列的区别就是可以不是连续的 因此无需对数字不等的情况进行处理 当前数字不等 当前为的数字就不能算在连续数组内了
					if (dp[i][j] > res)res = dp[i][j];//始终更新res为一个最大值
				}   //dp[i][j]表示在nums1中的(1,i)区间中和nums2中的(1,j)区间中的最长重复子数组的长度;
		}
		return res; //返回这个最大值
	}
};

Byte technical side

  1. Returns the subscript of the element that appears most in the array with equal probability
  2. Equal probability returns the subscript of the element that appears most in the array, in the form of an array, given the size of the array
  3. Determine whether tree a is a subtree of tree b
  4. Determine whether multiple linked lists have common child nodes

[Key points] The k numbers with the highest frequency of occurrence (the first highest frequency of occurrence == the highest number of occurrences)

Given a 数组numssum 一个整数k, return the elements with the k highest frequency of occurrence
Input: nums = [1,1,1,2,2,3], k = 2
Output:[1,2]

小顶堆实现
Small note:

//In the process of C++ development, we often use various containers of STL, such as vector, map, set, etc. These containers greatly facilitate our development.
//In the process of using these containers, the operations we will use a lot are insertion operations, such as push_back of vector, insert of map, and insert of set.
//These insert operations will be involved 两次构造,首先是对象的初始化构造,接着在插入的时候会复制一次,会触发拷贝构造.
//But in many cases, we don't need the waste of efficiency caused by two constructions. If we can directly construct it when inserting, we only need to construct it once.
//The C++11 standard already has such a syntax that can be used directly, that is ** emplace。只构造一次,节约内存,减少开销**;
vector has two functions that can be used: emplace, emplace_back. emplace is similar to insert, and emplace_back is similar to push_back.

insert image description here

The priority_queue is first in 队列, first in first out, adding elements at the end of the queue and deleting elements at the head of the queue ;
小顶堆:队首元素最小;;
大顶堆:队首元素最大

Code:

More concise code:

class Solution3 {
    
    
public:
	vector<int> topKFrequent(vector<int>& nums, int k) 
	{
    
    
		unordered_map<int, int> mp;//哈希表统计出现次数 
		for (int i : nums) ++mp[i];
		priority_queue<pair<int, int> > p;//默认大顶堆 降序排列 队首元素最大 堆里存放的是pair类型的数据
		for (auto iter=mp.begin();iter!=mp.end();iter++) //迭代器遍历哈希表
		{
    
    
			p.push({
    
     iter->second, iter->first });//这里把堆中元素的排序改为 按数字出现的次数排列,出现次数最多的元素在队首;
		}
		vector<int> ans;
		while (k--) 
		{
    
    
			ans.push_back(p.top().second);//把出现频率前k高的数字存入到容器中
			p.pop();
		}
		return ans;
	}
};

//哈希+小顶堆实现
class Solution
{
    
    
public:
	struct cmp
	{
    
    
		bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) //pair 参数一 数字 参数二 数字出现次数
		{
    
    
			return lhs.second > rhs.second;//按照数字出现次数实现从小到大的排列
		}
	};

	vector<int>topKFrequent(vector<int>& nums, int k)
	{
    
    
		unordered_map<int, int>mp;//哈希统计数字出现的次数
		for (auto& num : nums)
		{
    
    
			mp[num]++;
		}
		//Lamda表达式实现排序
		//auto cmp = [](const pair<int, int>& lhs, const pair<int, int>& rhs) {return lhs.second > rhs.second; };

		//小顶堆实现
		//priority_queue<pair<int, int>, vector<pair<int, int>>,greater<pair<int,int>>>heap;// 数据类型 保存数据的容器 元素比较方式

		priority_queue<pair<int, int>, vector<pair<int, int>>, cmp>heap;// 数据类型 保存数据的容器 元素比较方式(默认大顶堆 降序排列)

		for (auto it = mp.begin(); it != mp.end(); it++)//遍历哈希的方式
		{
    
    
			if (heap.size() < k) //先放入k个元素到堆中,堆中始终维护的是k个元素
			{
    
    
				heap.emplace(*it);//*it 表示当前遍历到的元素
			}
			else if (it->second > heap.top().second)//堆顶元素出现次数小于当前遍历到的元素的出现次数,始终保证堆中有k个出现频率最高的元素
			{
    
    
				heap.pop();//删除堆顶元素
				heap.emplace(*it);//插入当前遍历到的元素
			}
		}
		vector<int>res(k, 0);
		for (int i = k - 1; i >= 0; i--)//到序遍历堆 最后容器中元素是按从大到小的出现次数排布的 依次是出现次数第一高、第二高.....
		{
    
    
			res[i] = heap.top().first;
			heap.pop();
		}
		return res;
	}
};

int main()
{
    
    
	Solution so;
	vector<int>nums = {
    
     1,2,9,7,9,9,1,2,6 };
	//int n = nums.size();
	vector<int>ans;
	ans = so.topKFrequent(nums, 2);
	for (int i = 0; i < ans.size(); i++)
	{
    
    
		cout << "出现频率最高的k个元素是:"<<ans[i] << endl;
	}
	//cout << "出现频率最高的的元素是:" << ans[0] << endl;
	system("pause");
	return 0;
}

leetcode 451 Sort M according to the number of occurrences of characters

Given one 字符串 s, sort the characters in descending order according to their frequency of occurrence. The frequency of a character is the number of times it appears in the string.
Returns the sorted string. If there are multiple answers, return any of them.

Example:
Input: s = "tree"
Output: "eert"
Explanation: 'e' appears twice, 'r' and 't' both appear only once.
Therefore 'e' must appear before 'r' and 't'. Also, "eetr" is a valid answer.

Implementation idea: large top heap + hash table implementation

class Solution
{
    
    
public:
	string frequencySort(string s)
	{
    
    
		unordered_map<char, int>mp;
		string ans;
		priority_queue<pair<int, char>>q;//大顶堆实现
		for (auto str : s)mp[str]++;
		for (auto it = mp.begin(); it != mp.end(); it++)
		{
    
    
			q.push({
    
     it->second,it->first });//堆中第一个元素就是出现次数 第二个元素是对应的字符;
		}
		while(!q.empty())//遍历哈希中每一个元素
		{
    
    
			//这个for循环就是用来拼接某一个字符的 出现几次就循环几次
			for (int i = 0; i < q.top().first;i++) //对于多次出现的字符 实现重复拼接 避免只存储一次
			{
    
    
				ans+=q.top().second;
			}
			q.pop();
		}
		return ans;
	}
};

leetcode 1 sum of two numbers

Returns the subscript of these two numbers

class Solution {
    
    
public:
    vector<int> twoSum(vector<int>& nums, int target){
    
    
        int n=nums.size();
        vector<int>ret(2,0);
        unordered_map<int,int>table;//哈希表存储的数字和对应的下标
        for(int i=0;i<n;i++)
        {
    
    
            if(table.count(target-nums[i])>0)
            {
    
    
                ret[0]=i,ret[1]=table[target-nums[i]];
                return ret;
            }
            table[nums[i]]=i;
        }
        return ret;
    }
};

returns these two numbers


class Solution2
{
    
    
public:
	vector<int>twoSum(vector<int>& nums, int target)
	{
    
    
		unordered_map<int, int>mp;
		vector<int>res(2, 0);
		for (auto num : nums)
		{
    
    
			mp[num]++;
		}
		int n = nums.size();
		for (int i = 0; i < n; i++)
		{
    
    
			if (mp.count(target - nums[i]))
			{
    
    
				res[0]=nums[i];
				res[1] = target - nums[i];
			}
		}
		return res;
		
	}
};

int main()
{
    
    
	vector<int>nums = {
    
     2,5,8,9,1,9,3,7 };
	Solution2 so;
	vector<int>ans;
	ans = so.twoSum(nums, 10);
	cout << "这两个数字是:" << endl;
	cout<< ans[0] << endl;
	cout<< ans[1] << endl;
	system("pause");
	return 0;
}

repeated numbers in array

class Solution {
    
    
public:
    int findRepeatNumber(vector<int>& nums) 
    {
    
    
        unordered_map<int,bool>map;//bool 初始化为false
        for(int num:nums)
        {
    
    
            if(map[num])return num;//第二次出现才会置为true
            map[num]=true;
        }
        return -1;

    }
};


leetcode 120 Triangle minimum path and M

Given a 三角形triangleFind the minimum path sum from top to bottom;
each step can only move to the adjacent node in the next row. 相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。That is, if it is at subscript i of the current row, then the next step can move to subscript i or i+1 of the next row.

For example:
Input: triangle = [[2],[3,4],[6,5,7],[4,1,8,3]]
Output: 11
Explanation: As shown in the diagram below:
2
3 4
6 5 7
4 1 8 3
The minimum path sum from top to bottom is 11 (ie, 2 + 3 + 5 + 1 = 11).

动态规划The deformation solution is searched from bottom to top (start from the penultimate line)

class Solution
{
    
    
public:
	int miniumTotal(vector<vector<int>>& triangle)
	{
    
    
		int m = triangle.size();//三角形的行数
		for (int i=m-2; i >= 0; i--)//从倒数第二行开始遍历
		{
    
    
			int n = triangle[i].size();//三角形每一行的元素
			for (int j = 0; j < n; j++)//遍历每一行的每个元素
			{
    
    
				triangle[i][j] += min(triangle[i + 1][j], triangle[i + 1][j + 1]);//第一次遍历就是从倒数第二行开始 寻找最后一行的最小值;
			}
		}
		return triangle[0][0];//返回三角形顶尖的元素就是存储的最小路径
	}
};


leetcode10 regular expression matching H

Give you one 字符串 s 和一个字符规律 p, please implement a regular expression matching that supports '.' and '*'.

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素

The so-called match is to cover the entire string s, not part of the string.

Dynamic programming solution


class Solution
{
    
    
public:
	bool isMatch(string s, string p)
	{
    
    
		int m = s.size();
	    int n = p.size();
		//Lamda表达式
		auto matches = [&](int i, int j) {
    
    
			if (i == 0)return false;
			if (p[j - 1] == '.')return true;
			return s[i - 1] == p[j - 1];
		};
		vector<vector<int>>dp(m + 1, vector<int>(n + 1));//创建一个m+1行 n+1列的二维数组
		dp[0][0] = true;
		for (int i = 0; i <= m; i++)
		{
    
    
			for (int j = 1; j <= n; j++)
			{
    
    
				if (p[j - 1] == '*')
				{
    
    
					dp[i][j] |= dp[i][j - 2];  // |=按位或并赋值  按位或 |  按位或只要有一位为1,结果就为1,都为0就为0
					if (matches(i, j - 1))
					{
    
    
						dp[i][j] |= dp[i - 1][j];
					}
				}
				else
				{
    
    
					if (matches(i, j))
					{
    
    
						dp[i][j] |= dp[i - 1][j - 1];
					}
				}
			}
		}
		return dp[m][n];
	}
};

Guess you like

Origin blog.csdn.net/weixin_48433164/article/details/123753237