本篇博客旨在整理记录自己刷的一些基础题的思路、代码以及注解,同时希望可给小伙伴一些帮助。本人也是算法小白,水平有限,如果文章中有什么错误之处,希望小伙伴们可以在评论区指出来,共勉 💪。
文章目录
一、双指针
415. 字符串相加
难度:简单
题目:给定两个字符串形式的非负整数 num1
和num2
,计算它们的和并同样以字符串形式返回。
你不能使用任何內建的用于处理大整数的库(比如 BigInteger
), 也不能直接将输入的字符串转换为整数形式。
提示:
- 1 <= num1.length, num2.length <= 104
num1
和num2
都只包含数字0-9
num1
和num2
都不包含任何前导零
解题代码:
class Solution {
public String addStrings(String num1, String num2) {
StringBuilder s = new StringBuilder();
//用来记录进位
int w = 0;
//记录两个字符串的长度
int len1 = num1.length(),len2 = num2.length();
//对应俩字符串长度的最小和最大值
int mn = Math.min(len1,len2),mx = Math.max(len1,len2);
//遍历两个字符串尾部对齐的共同部分
for(int m = 0; m < mn;m++){
//获取第一个字符串最后一位对应的数值,同时指针往前移动一步
int i = num1.charAt(len1 - 1) - '0';
len1--;
//获取第二个字符串最后一位对应的数值,同时指针往前移动一步
int j = num2.charAt(len2 -1) - '0';
len2--;
int sum = i + j + w;
//获取进位
w = sum / 10;
//加入到结果字符串中
s.append(sum % 10);
}
//遍历剩余较长的字符串的前半部分
for(int n = 0;n < mx - mn; n++){
if(len1 > 0){//第一个字符串较长的情况
int i = num1.charAt(len1 - 1) - '0';
len1--;
int sum = i + w;
w = sum / 10;
s.append(sum % 10);
}else if(len2 > 0){//第二个字符串较长的情况
int j = num2.charAt(len2 -1) - '0';
len2--;
int sum = j + w;
w = sum / 10;
s.append(sum % 10);
}
}
//无论哪个更长或者相同,最后都可能产生进位,要判断是否产生进位,加上进位
if(w != 0){
s.append(w);
}
//因为是正方向防止的要逆序,而且要转换为String类型
return s.reverse().toString();
}
}
167. 两数之和 II - 输入有序数组
难度:中等
题目:给你一个下标从 1 开始的整数数组 numbers ,该数组已按非递减顺序排列,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。
以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
你所设计的解决方案必须只使用常量级的额外空间。
提示:
- 2 <= numbers.length <= 3 * 104
- -1000 <= numbers[i] <= 1000
numbers
按非递减顺序排列- -1000 <= target <= 1000
- 仅存在一个有效答案
解题代码:
class Solution {
public int[] twoSum(int[] numbers, int target) {
/**题意:找出数组中两个数之和为target并返回下标,要求 1 <= index1 < index2 <=numbers.length*/
// 判断数组为空,则直接返回
if (numbers == null) return null;
// 定义左右两指针
int i = 0, j = numbers.length - 1;
while (i < j) {
// 求和
int sum = numbers[i] + numbers[j];
// 判断是否相等,相等则返回,反之则递减/递增
if (sum == target) {
return new int[]{i+1, j+1};
}else if (sum < target) {
i++;
} else {
j--;
}
}
return null;
}
}
1234. 替换子串得到平衡字符串
难度:中等
题目:有一个只含有 'Q', 'W', 'E', 'R'
四种字符,且长度为 n
的字符串。
假如在该字符串中,这四个字符都恰好出现 n/4
次,那么它就是一个「平衡字符串」。
给你一个这样的字符串 s
,请通过「替换一个子串」的方式,使原字符串 s
变成一个「平衡字符串」。
你可以用和「待替换子串」长度相同的 任何 其他字符串来完成替换。
请返回待替换子串的最小可能长度。
如果原字符串自身就是一个平衡字符串,则返回 0
。
提示:
- 1 <= s.length <= 105
s.length
是4
的倍数s
中只含有'Q'
,'W'
,'E'
,'R'
四种字符
解题代码:
class Solution {
public int balancedString(String s) {
//滑动窗口
int[] counts = new int[26]; // 使用数组来存储四个字符的出现次数(使用数组便于代码书写)
int len = s.length(); // 字符串长度
int limit = len / 4; // n/4
char ch;
// 初始化不替换内容字符出现次数数组,即初始滑动窗口维护一个空串
for(int i = 0;i < len;i++) {
ch = s.charAt(i);
counts[ch - 'A']++;
}
// 初始化滑动窗口左右指针,维护的子串是[left,right]的内容
// 初始化子串为空,因此left=0,right=-1表示一个空字串
int left = 0;
int right = -1;
int minLength = len; // 最小替换子串长度,初始为整个字符串长度
// 滑动窗口
while(left < len) {
if(check(counts, limit)) { // 效验通过
// 记录当前合法子串的长度并更新最小长度
// 左指针右移,那么原本左指针指向的字符就变成不替换的内容,不替换的内容少了一个字符,对应count数组中的值加1
minLength = Math.min(minLength, right - left + 1);
counts[s.charAt(left++) - 'A']++;
}else if(right < len - 1) {
// 当前子串不合法且右指针还没到头
// 右指针右移,移动后的右指针指向的字符变成了子串的内容,不替换的内容少了一个字符,对应count数组中的值减1
counts[s.charAt(++right) - 'A']--;
}else {
// 右指针到头,搜索结束
break;
}
}
return minLength;
}
/**
效验函数,效验当前counts中四个字符的出现次数是否都小于等于limit;
是返回true,否返回false
*/
private boolean check(int[] counts, int limit) {
if(counts['Q' - 'A'] > limit || counts['W' - 'A'] > limit || counts['E' - 'A'] > limit || counts['R' - 'A'] > limit) {
return false;
}
return true;
}
}
209. 长度最小的子数
难度:中等
题目:给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其和 ≥ target
的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度**。**如果不存在符合条件的子数组,返回 0
。
提示:
- 1 <= target <= 109
- 1 <= nums.length <= 105
- 1 <= nums[i] <= 105
解题代码:
class Solution {
public int minSubArrayLen(int target, int[] nums) {
int left = 0; // 滑动窗口起始位置
int sum = 0; // 滑动窗口数值之和
int result = Integer.MAX_VALUE;
for (int right = 0; right < nums.length; right++) {
sum += nums[right];
// 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
while (sum >= target) {
result = Math.min(result,right - left + 1);// 取子序列长度二者的最小值
sum -= nums[left++]; // 这里为滑动窗口的精髓之处,不断变更i(子序列的起始位置)
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列。
return result == Integer.MAX_VALUE ? 0 :result;
}
}
76. 最小覆盖子串
难度:困难
题目:给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。如果 s 中不存在涵盖 t 所有字符的子串,则返回空字符串 “” 。
注意:
- 对于 t 中重复字符,我们寻找的子字符串中该字符数量必须不少于 t 中该字符数量。
- 如果 s 中存在这样的子串,我们保证它是唯一的答案。
提示:
m == s.length
n == t.length
- 1 <= m, n <= 105
s
和t
由英文字母组成
解题代码:
class Solution {
public String minWindow(String s, String t) {
if (s == null || s.length() == 0 || t == null ||t.length() == 0){
return "";
}
int[] need = new int[128];
//记录需要的字符的个数
for (int i = 0;i < t.length(); i++) {
need[t.charAt(i)]++;
}
//l是当前左边界,r是当前右边界,size记录窗口大小,count是需要的字符个数,start是最小覆盖串开始的index
int l = 0,r = 0,size = Integer.MAX_VALUE,count = t.length(),start = 0;
//遍历所有字符
while(r < s.length()) {
char c = s.charAt(r);
if (need[c] > 0){//需要字符c
count--;
}
need[c]--;//把右边的字符加入窗口
if(count == 0) {//窗口中已经包含所有字符
while (l < r && need[s.charAt(l)] < 0) {
need[s.charAt(l)]++;//释放右边移动出窗口的字符
l++;//指针右移
}
if(r - l + 1 < size) {//不能右移时候挑战最小窗口大小,更新最小窗口开始的start
size = r - l + 1;
start = l;//记录下最下值时候的开始位置,最后返回复盖串时候会用到
}
//l向右移动后窗口肯定不能满足了,重新开始循环
need[s.charAt(l)]++;
l++;
count++;
}
r++;
}
return size == Integer.MAX_VALUE ? "" : s.substring(start, start + size);
}
}
二、模拟
2347. 最好的扑克手牌
难度:简单
题目:给你一个整数数组 ranks
和一个字符数组 suit
。你有 5
张扑克牌,第 i
张牌大小为 ranks[i]
,花色为 suits[i]
。
下述是从好到坏你可能持有的 手牌类型 :
"Flush"
:同花,五张相同花色的扑克牌。"Three of a Kind"
:三条,有 3 张大小相同的扑克牌。"Pair"
:对子,两张大小一样的扑克牌。"High Card"
:高牌,五张大小互不相同的扑克牌。
请你返回一个字符串,表示给定的 5 张牌中,你能组成的 最好手牌类型 。
注意: 返回的字符串 大小写 需与题目描述相同。
提示:
ranks.length == suits.length == 5
1 <= ranks[i] <= 13
'a' <= suits[i] <= 'd'
- 任意两张扑克牌不会同时有相同的大小和花色。
解题代码:
class Solution {
public String bestHand(int[] ranks, char[] suits) {
char suit = suits[0]; // 记录首个花色
// 遍历所有手牌的花色
for(char s: suits) {
// 判断是否有不同得花色
if (s != suit) {
// 花色不同就将 s 对应的花色赋值给 suit
suit = s;
break;
}
}
// 判断五个花色是否相同,全部相同时输出 Flush
if(suit == suits[0]) {
return "Flush";
}
// 定义统计手牌大小的数组
int[] counts = new int[13];
// 遍历统计每个数字出现的次数
for(int num: ranks) {
// 手牌的值对应的数组下标自增
counts[num - 1]++;
}
// 找出出现次数最多的次数
int maxCount = 1;
// 遍历统计好手牌大小出现次数
for(int count: counts){
//通过函数找出出现最多的手牌大小
maxCount = Math.max(count, maxCount);
}
// 判断是否为 三条
if (maxCount >= 3) {
return "Three of a Kind";
}else if (maxCount == 2){
// 判断是否为 对子
return "Pair";
}else {
//判断是否为 高牌
return "High Card";
}
}
}
剑指 Offer 29. 顺时针打印矩阵
难度:简单
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
限制:
0 <= matrix.length <= 100
0 <= matrix[i].length <= 100
思路
根据题意可以知道打印顺序是 “从左向右、从上向下、从右向左、从下向上” 循环,同时需要考虑到“上、下、左、右”边界问题。
当它为空时,直接返回;否则按顺序打印一维数组输出。
解题代码:
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length == 0) return new int[0];
//定义l为左边、r为右边、t为上边、b为下边,x为计数器
int l = 0,r = matrix[0].length - 1,t = 0,b = matrix.length - 1,x = 0;
int[] res = new int[(r + 1) * (b + 1)];
while(true) {
//列在变,列为循环值
for (int i = l; i <= r; i++) {
res[x++] = matrix[t][i]; // 从左到右
}
//从左往右的下一步是往下走,上边界内缩,故++t
if(++t > b) break;
//从上往下,行在变
for (int i = t; i <= b; i++) {
res[x++] = matrix[i][r]; // 从上到下
}
//从上往下的下一步是从右往左,右边界收缩,--r
if(l > --r) break;
//从右向左,列在变
for (int i = r;i >= l; i--) {
res[x++] = matrix[b][i]; // 从右到左
}
//从右往左的下一步是从下往上,下边界收缩,--b
if(t > --b) break;
//从下到上,行在变
for (int i = b; i >= t; i--) {
res[x++] = matrix[i][l]; // 从下到上
}
//从下到上的下一步是从左到右,左边界收缩,++l
if(++l > r) break;
}
return res;
}
}
2319. 判断矩阵是否是一个 X 矩阵
难度:简单
题目:如果一个正方形矩阵满足下述 全部 条件,则称之为一个 X 矩阵 :
- 矩阵对角线上的所有元素都 不是 0
- 矩阵中所有其他元素都是 0
给你一个大小为 n x n
的二维整数数组 grid
,表示一个正方形矩阵。如果 grid
是一个 X 矩阵 ,返回 true
;否则,返回 false
。
提示:
n == grid.length == grid[i].length
3 <= n <= 100
- 0 <= grid[i] [j] <= 105
思路
-
对角线上点对应当前坐标 (i,j) 的判断:
-
如果 i==j,说明在形如 \ 正对角线上
-
如果 i==n-j-1,说明在形如 / 的反对角线上
-
-
对角线上为 0 的点或者非对角线上不为 0 的点是非法点,返回 false
-
其他情况返回 true
解题代码
class Solution {
public boolean checkXMatrix(int[][] grid) {
int n = grid.length;
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++){
if((i==j||i==n-j-1)){
if(grid[i][j]==0){
return false;
}
}else if(grid[i][j] != 0){
return false;
}
}
}
return true;
}
}
2315. 统计星号
难度:简单
题目:给你一个字符串 s
,每 两个 连续竖线 '|'
为 一对 。换言之,第一个和第二个 '|'
为一对,第三个和第四个 '|'
为一对,以此类推。
请你返回 不在 竖线对之间,s
中 '*'
的数目。
注意,每个竖线 '|'
都会 恰好 属于一个对。
提示:
1 <= s.length <= 1000
s
只包含小写英文字母,竖线'|'
和星号'*'
。s
包含 偶数 个竖线'|'
。
模拟
- 从左向右遍历,如果找到奇数个竖线,则接下来找到的星号不计数,找到偶数个竖线,则接下来的星号计数
- 第一个竖线前的星号计数,后面通过找到竖线的奇偶性,变动计数开关,来达到只统计竖线外星号的目的。初始计数数目为 cnt=1,每次遇到一个竖线后,个位取反,可以用异或:cnt = cnt^1 或者直接减:cnt=1-cnt 的方式来变动计数开关
- 遇到星号时,直接加计数开关即可
class Solution {
public int countAsterisks(String s) {
int cnt = 1;
int n = s.length();
int ans = 0;
for(int i = 0;i < n;i++){
char c = s.charAt(i);
if(c == '|' ){
cnt = 1-cnt;
}else if(c == '*'){
ans += cnt;
}
}
return ans;
}
}
三、动态规划
剑指 Offer 47. 礼物的最大价值
难度:中等
题目:在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?
提示:
0 < grid.length <= 200
0 < grid[0].length <= 200
思路:
题意:从棋盘左上角,每次向右或向下移动一格,直到棋盘右下角,求可获得的最大价值。
设f(i,j) 代表从左上角到达单元格 (i,j) 时,可拿的最大累计价值;gird(i-1, j-1) 为上一单元格价值。
dp 方程为:f(i,j) = Math.max(f(i-1, j), f(i, j-1)) + grid(i-1,j-1);
解题代码:
class Solution {
public int maxValue(int[][] grid) {
// 初始化棋盘行列
int row = grid.length;
int column = grid[0].length;
// dp[i][j] 表示从 grid[0][0] 到grid[i-1][j-1]时的最大价值
int[][] dp = new int[row + 1][column + 1];
// 从[1,1]开始遍历,累加上初始价值大小
for (int i = 1; i <= row; i++) {
for (int j = 1;j <= column; j++) {
// dp 方程
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1];
}
}
// 因为row和column为棋盘行和列,所以可为数组的length不越界。
return dp[row][column];
}
}
5. 最长回文子串
难度:中等
题目:给你一个字符串 s
,找到 s
中最长的回文子串。
如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。
提示:
1 <= s.length <= 1000
s
仅由数字和英文字母组成
解题代码:
class Solution {
public String longestPalindrome(String s) {
//动态规划
if (s == null || s.length() < 2) {
return s;
}
int strLen = s.length();
int maxStart = 0; //最长回文串的起点
int maxEnd = 0; //最长回文串的终点
int maxLen = 1; //最长回文串的长度
boolean[][] dp = new boolean[strLen][strLen];
for (int r = 1; r < strLen; r++) {
for (int l = 0; l < r; l++) {
if (s.charAt(l) == s.charAt(r) && (r - l <= 2 || dp[l + 1][r - 1])) {
// 初始状态,1=r时,此时 dp[1][r] = true
dp[l][r] = true;
// 状态转移方程,dp[1][r] = true 并且(1-1)和(r+1)两个位置为相同的字符,此时 dp[1-1][r+1] = true.
if (r - l + 1 > maxLen) {
maxLen = r - l + 1;
maxStart = l;
maxEnd = r;
}
}
}
}
return s.substring(maxStart, maxEnd + 1);
}
}
拓展:中心扩展算法
class Solution {
public String longestPalindrome(String s) {
//中心扩展算法
if (s==null ||s.length()==0){return "";}
//获取字符串长度 用于循环
int length = s.length();
//变量存储 i 的下标
int left=0;
int right=0;
// 最大长度,只有一个时为1 ,所以初始值为1;
int maxlen=0;
//获取切割字段的起始位置
int maxStart=0;
for (int i =0 ; i<length;i++){
//记录循环长度 ,自动刷新
int len =1;
//获取当前位置的左右位置的下标
left = i-1;
right= i+1;
//左扩散
while (left>=0 && s.charAt(left)==s.charAt(i)){
len++; left--;
}
//右扩散 最后一位也算入
while (right<length && s.charAt(right)==s.charAt(i)){
len++; right++;
}
//左右扩散 ,左边的值等于右边
while (right<length && left>=0 && s.charAt(right)==s.charAt(left)){
len+=2; right++; left--;
}
if (len>maxlen){
maxlen=len;
//因为每完成一次 ,left--,所以最后合格的一次也--,导致开始位置向后移动一位
maxStart = left+1;
}
}
return s.substring(maxStart,maxlen+maxStart);
}
}
474. 一和零
难度:中等
题目:给你一个二进制字符串数组 strs
和两个整数 m
和 n
。
请你找出并返回 strs
的最大子集的长度,该子集中 最多 有 m
个 0
和 n
个 1
。
如果 x
的所有元素也是 y
的元素,集合 x
是集合 y
的 子集 。
提示:
1 <= strs.length <= 600
1 <= strs[i].length <= 100
strs[i]
仅由'0'
和'1'
组成1 <= m, n <= 100
解题代码:
class Solution {
public int findMaxForm(String[] strs, int m, int n) {
//动态规划
//确定dp数组(dp table)以及下标的含义 dp[i][j]:最多有i个0和j个1的strs的最大子集的大小为dp[i][j]
//确定递推公式 dp[i][j]就是dp[i - zeroNum][j - oneNum] + 1 : dp[i][j] = Math.max(dp[i][j],dp[j - weight[i] + value[i]]);
//dp数组如何初始化,初始为0
//确定遍历顺序 遍历背包容量且从后向前的遍历!
int[][] dp = new int[m+1][n+1];
dp[0][0] = 0;
for (String str:strs) { //遍历物品
int[] zeroAndOne = calcZeroAndOne(str);
int zeroNum = zeroAndOne[0];
int oneNum = zeroAndOne[1];
for (int i = m;i >= zeroNum;i--) {//遍历背包容量且从后向前遍历
for (int j = n;j >= oneNum;j--){
dp[i][j] = Math.max(dp[i][j],dp[i - zeroNum][j - oneNum] + 1);
}
}
}
return dp[m][n];
}
private static int[] calcZeroAndOne(String str) {
int[] res = new int[2];
for (char c : str.toCharArray()) {
res[c - '0']++;
}
return res;
}
}
494. 目标和
难度:中等
题目:给你一个整数数组 nums
和一个整数 target
。
向数组中的每个整数前添加 '+'
或 '-'
,然后串联起所有整数,可以构造一个 表达式 :
- 例如,
nums = [2, 1]
,可以在2
之前添加'+'
,在1
之前添加'-'
,然后串联起来得到表达式"+2-1"
。
返回可以通过上述方法构造的、运算结果等于 target
的不同 表达式 的数目。
提示:
1 <= nums.length <= 20
0 <= nums[i] <= 1000
0 <= sum(nums[i]) <= 1000
-1000 <= target <= 1000
解题代码(动态规划):
class Solution {
public int findTargetSumWays(int[] nums, int target) {
int sum = 0;
//统计整数数组中所有数的和
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
//绝对值范围超过了sum的绝对值范围则无法得到
}
//判断两者的绝对值大小
if (Math.abs(target) > Math.abs(sum)) {
return 0;
}
int len = nums.length;
int range = sum * 2 + 1;//因为要包含负数所以要两倍,又要加上0这个中间的那个情况
int[][] dp = new int[len][range];//这个数组是从总和为-sum开始的
//加上sum纯粹是因为下标界限问题,赋第二维的值的时候都要加上sum
// 初始化 第一个只能分别组成+-nums[i]的一种情况
dp[0][sum + nums[0]] += 1;
dp[0][sum - nums[0]] += 1;
for (int i = 1; i < len; i++) {
for (int j = -sum; j <= sum; j++) {
if ((j+nums[i]) > sum) {//+不成立 加上当前数大于了 sum 只能减去当前的数
dp[i][j+sum] = dp[i - 1][j - nums[i] + sum] + 0;
}else if((j - nums[i]) < -sum) { //-不成立, 减去当前数小于-sum 只能加上当前的数
dp[i][j+sum] = dp[i-1][j+nums[i]+sum] + 0;
}else { // +-都可以
dp[i][j+sum] = dp[i - 1][j + nums[i] + sum] + dp[i-1][j-nums[i] + sum];
}
}
}
return dp[len - 1][sum + target];
}
}
解题代码(01背包):
class Solution {
public int findTargetSumWays(int[] nums, int target) {
/*类01背包问题:
设pos为取+的数字和,neg为取-的数字和(均为正数),则target = pos - neg=pos - (sum - pos) = 2*pos-sum
故pos=(sum + target)/ 2 >= 0且为常数,因此此问题等价于求有多少种方法用nums[i]凑成和为pos
进而该问题抽象为:用价值与体积均为nums[i]的物品,恰好凑满容量为pos的背包方案数
1、动态定义:dp[i]为恰好能凑满容量为j的背包方案数
2、状态转移:背包容量能或者不能装下nums[i]
2.1当不能装下nums[i]时,方案数直接继承之前的dp[j]
2.2当能装下nums[i]时,总的方案数为不考虑nums[i]的方案数 + 有nums[i]参与新增的方案数
dp[j] += dp[j - nums[i]],dp[j - nums[i]]种方案与nums[i]共同凑成pos,即1*dp[j - nums[i]]
3、状态初始化:dp[0] = 1,因为后面总会一直查找至j = 0,如dp[3] += dp[3 - 3],空集是任意一条有效路径的起点,当属一条
4、遍历顺序:i正序,j倒序
5、返回形式:dp[pos]就是凑成pos总的方案数
*/
int len = nums.length;
int sum = 0;
for (int num : nums) sum += num;
//pos为小数 || target绝对值比sum还大
if((sum + target) % 2 == 1 || Math.abs(target) > sum) return 0;
int pos = (sum + target) / 2;
int[] dp = new int[pos + 1];
dp[0] = 1;
for (int num : nums) {
for (int j = pos; j >= num; j--) {
dp[j] += dp[j - num];
}
}
return dp[pos];
}
}
最后
希望可以点赞❤️+收藏⭐,谢谢各位大佬~~🙌🙌🙌