算法&&八股文&&其他
前言:一个月说长不长,说短不短,然而到现在仍然是实习0offer状态,23届真的是太难了,一方面投的比较晚,另一方面自己确实还有许多不足,无论是技术储备还是表达能力都需要不断训练加强。在此也给各位小伙伴提个醒:一定要早投简历!不要总觉得没准备好,等你觉得准备好hc也没了。总之学习只有进行时没有完成时,建议边投边学边总结🤔
------------------------------- Stay hungry, Stay foolish 🏃 -------------------------------
一、算法篇
- 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指在第 i 天之后,才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
分析:middle(lc739)
单调栈:可以维护一个存储下标的单调栈,从栈底到栈顶的下标对应的温度列表中的温度依次递减。如果一个下标在单调栈里,则表示尚未找到下一次温度更高的下标。
正向遍历温度列表。对于温度列表中的每个元素 t e m p e r a t u r e s [ i ] temperatures[i] temperatures[i],如果栈为空,则直接将 i i i 进栈,如果栈不为空,则比较栈顶元素 p r e v I n d e x prevIndex prevIndex 对应的温度 t e m p e r a t u r e s [ p r e v I n d e x ] temperatures[prevIndex] temperatures[prevIndex] 和当前温度 t e m p e r a t u r e s [ i ] temperatures[i] temperatures[i],如果 t e m p e r a t u r e s [ i ] > t e m p e r a t u r e s [ p r e v I n d e x ] temperatures[i] > temperatures[prevIndex] temperatures[i]>temperatures[prevIndex],则将 p r e v I n d e x prevIndex prevIndex 移除,并将 p r e v I n d e x prevIndex prevIndex 对应的等待天数赋为 i − p r e v I n d e x i - prevIndex i−prevIndex,重复上述操作直到栈为空或者栈顶元素对应的温度小于等于当前温度,然后将 i i i 进栈。
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int n = temperatures.size();
vector<int> ans(n);
stack<int> s;
for (int i = 0; i < n; ++i) {
while (!s.empty() && temperatures[i] > temperatures[s.top()]) {
int previousIndex = s.top();
ans[previousIndex] = i - previousIndex;
s.pop();
}
s.push(i);
}
return ans;
}
};
- 给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
分析:简单(lc151)
排序 + 双指针
我们首先考虑枚举第一个元素 a a a,对于剩下的两个元素 b b b 和 c c c,我们希望它们的和最接近 t a r g e t − a target−a target−a。对于 b b b 和 c c c,如果它们在原数组中枚举的范围(既包括下标的范围,也包括元素值的范围)没有任何规律可言,那么我们还是只能使用两重循环来枚举所有的可能情况。因此,我们可以考虑对整个数组进行升序排序,这样一来:
- 假设数组的长度为 n n n,我们先枚举 a a a,它在数组中的位置为 i i i;
- 为了防止重复枚举,我们在位置 [ i + 1 , n ) [i+1,n) [i+1,n) 的范围内枚举 b b b 和 c c c。
当我们知道了 b b b 和 c c c 可以枚举的下标范围,并且知道这一范围对应的数组元素是有序(升序)的,借助双指针,我们就可以对枚举的过程进行优化。我们用 p b p_b pb 和 p c p_c pc 分别表示指向 b b b 和 c c c 的指针,初始时, p b p_b pb 指向位置 i + 1 i+1 i+1,即左边界; p c p_c pc 指向位置 n − 1 n-1 n−1,即右边界。在每一步枚举的过程中,我们用 a + b + c a+b+c a+b+c 来更新答案,并且:
- 如果 a + b + c ≥ target a+b+c \geq \textit{target} a+b+c≥target,那么就将 p c p_c pc 向左移动一个位置;
- 如果 a + b + c < target a+b+c < \textit{target} a+b+c<target,那么就将 p b p_b pb 向右移动一个位置。
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
nums.sort()
n = len(nums)
best = 10**7
# 根据差值的绝对值来更新答案
def update(cur):
nonlocal best
if abs(cur - target) < abs(best - target):
best = cur
# 枚举 a
for i in range(n):
# 保证和上一次枚举的元素不相等
if i > 0 and nums[i] == nums[i - 1]:
continue
# 使用双指针枚举 b 和 c
j, k = i + 1, n - 1
while j < k:
s = nums[i] + nums[j] + nums[k]
# 如果和为 target 直接返回答案
if s == target:
return target
update(s)
if s > target:
# 如果和大于 target,移动 c 对应的指针
k0 = k - 1
# 移动到下一个不相等的元素
while j < k0 and nums[k0] == nums[k]:
k0 -= 1
k = k0
else:
# 如果和小于 target,移动 b 对应的指针
j0 = j + 1
# 移动到下一个不相等的元素
while j0 < k and nums[j0] == nums[j]:
j0 += 1
j = j0
return best
- 一条包含字母 A-Z 的消息通过以下映射进行了 编码 :
‘A’ -> “1”
‘B’ -> “2”
…
‘Z’ -> “26”
要 解码 已编码的消息,所有数字必须基于上述映射的方法,反向映射回字母(可能有多种方法)。例如,“11106” 可以映射为:
“AAJF” ,将消息分组为 (1 1 10 6)
“KJF” ,将消息分组为 (11 10 6)
注意,消息不能分组为 (1 11 06) ,因为 “06” 不能映射为 “F” ,这是由于 “6” 和 “06” 在映射中并不等价。
给你一个只含数字的 非空 字符串 s ,请计算并返回解码方法的总数 。
题目数据保证答案肯定是一个 32 位 的整数。
分析:简单(lc91)
动态规划:对于给定的字符串 s s s,设它的长度为 n n n,其中的字符从左到右依次为 s [ 1 ] , s [ 2 ] , ⋯ , s [ n ] s[1], s[2], \cdots, s[n] s[1],s[2],⋯,s[n]。我们可以使用动态规划的方法计算出字符串 s s s 的解码方法数。具体地,设 f i f_i fi 表示字符串 s s s 的前 i i i 个字符 s [ 1.. i ] s[1..i] s[1..i] 的解码方法数。在进行状态转移时,我们可以考虑最后一次解码使用了 s s s 中的哪些字符,那么会有下面的两种情况:
- 第一种情况是我们使用了一个字符,即 s [ i ] s[i] s[i] 进行解码,那么只要 s [ i ] ≠ 0 s[i] \neq 0 s[i]=0,它就可以被解码成 A ∼ I \text{A} \sim \text{I} A∼I 中的某个字母。由于剩余的前 i − 1 i−1 i−1 个字符的解码方法数为 f i − 1 f_{i-1} fi−1,因此我们可以写出状态转移方程:
f i = f i − 1 , 其 中 s [ i ] ≠ 0 f_i = f_{i-1}, \quad 其中 ~ s[i] \neq 0 fi=fi−1,其中 s[i]=0- 第二种情况是我们使用了两个字符,即 s [ i − 1 ] s[i−1] s[i−1] 和 s [ i ] s[i] s[i] 进行编码。与第一种情况类似, s [ i − 1 ] s[i−1] s[i−1] 不能等于 0 0 0,并且 s [ i − 1 ] s[i-1] s[i−1] 和 s [ i ] s[i] s[i] 组成的整数必须小于等于 26 26 26,这样它们就可以被解码成 J ∼ Z \text{J} \sim \text{Z} J∼Z 中的某个字母。由于剩余的前 i − 2 i−2 i−2 个字符的解码方法数为 f i − 2 f_{i-2} fi−2 ,因此我们可以写出状态转移方程:
f i = f i − 2 , 其 中 s [ i − 1 ] ≠ 0 并 且 10 ⋅ s [ i − 1 ] + s [ i ] ≤ 26 f_i = f_{i-2}, \quad 其中 ~ s[i-1] \neq 0 ~并且~ 10\cdot s[i-1]+s[i] \leq 26 fi=fi−2,其中 s[i−1]=0 并且 10⋅s[i−1]+s[i]≤26
需要注意的是,只有当 i > 1 i>1 i>1 时才能进行转移,否则 s [ i − 1 ] s[i−1] s[i−1] 不存在。
将上面的两种状态转移方程在对应的条件满足时进行累加,即可得到 f i f_i fi 的值。在动态规划完成后,最终的答案即为 f n f_n fn。
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
// a = f[i-2], b = f[i-1], c = f[i]
int a = 0, b = 1, c;
for (int i = 1; i <= n; ++i) {
c = 0;
if (s[i - 1] != '0') {
c += b;
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
c += a;
}
tie(a, b) = {b, c};
}
return c;
}
};
- 在一条环路上有 n 个加油站,其中第 i 个加油站有汽油 gas[i] 升。
你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发,开始时油箱为空。
给定两个整数数组 gas 和 cost ,如果你可以绕环路行驶一周,则返回出发时加油站的编号,否则返回 -1 。如果存在解,则 保证 它是 唯一 的。
分析:middle(lc134)
贪心:假设我们此前发现,从加油站 x x x 出发,每经过一个加油站就加一次油(包括起始加油站),最后一个可以到达的加油站是 y y y(不妨设 x < y x<y x<y)。这就说明:
∑ i = x y gas [ i ] < ∑ i = x y cost [ i ] ∑ i = x j g a s [ i ] ≥ ∑ i = x j c o s t [ i ] (For all j ∈ [ x , y ) ) \sum_{i=x}^{y}\textit{gas}[i] < \sum_{i=x}^{y}\textit{cost}[i] \\ \sum_{i=x}^{j}gas[i] \ge \sum_{i=x}^{j}cost[i] ~ \text{(For all $j \in [x, y)$) } i=x∑ygas[i]<i=x∑ycost[i]i=x∑jgas[i]≥i=x∑jcost[i] (For all j∈[x,y))
第一个式子表明无法到达加油站 y y y 的下一个加油站,第二个式子表明可以到达 y y y 以及 y y y 之前的所有加油站。
现在,考虑任意一个位于 x , y x,y x,y 之间的加油站 z z z(包括 x x x 和 y y y),我们现在考察从该加油站出发,能否到达加油站 y y y 的下一个加油站,也就是要判断 ∑ i = z y gas [ i ] \sum_{i=z}^{y}\textit{gas}[i] ∑i=zygas[i] 与 ∑ i = z y cost [ i ] \sum_{i=z}^{y}\textit{cost}[i] ∑i=zycost[i] 之间的大小关系。
根据上面的式子,我们得到:
∑ i = z y gas [ i ] = ∑ i = x y gas [ i ] − ∑ i = x z − 1 gas [ i ] < ∑ i = x y cost [ i ] − ∑ i = x z − 1 gas [ i ] < ∑ i = x y cost [ i ] − ∑ i = x z − 1 c o s t [ i ] = ∑ i = z y cost [ i ] \begin{aligned} \sum_{i=z}^{y}\textit{gas}[i]&=\sum_{i=x}^{y}\textit{gas}[i]-\sum_{i=x}^{z-1}\textit{gas}[i] \\ &< \sum_{i=x}^{y}\textit{cost}[i]-\sum_{i=x}^{z-1}\textit{gas}[i] \\ &< \sum_{i=x}^{y}\textit{cost}[i]-\sum_{i=x}^{z-1}cost[i] \\ &= \sum_{i=z}^{y}\textit{cost}[i] \end{aligned} i=z∑ygas[i]=i=x∑ygas[i]−i=x∑z−1gas[i]<i=x∑ycost[i]−i=x∑z−1gas[i]<i=x∑ycost[i]−i=x∑z−1cost[i]=i=z∑ycost[i]
其中不等式的第二步、第三步分别利用了上面的第一个、第二个不等式。
从上面的推导中,能够得出结论:从 x , y x,y x,y 之间的任何一个加油站出发,都无法到达加油站 y y y 的下一个加油站。
在发现了这一个性质后,算法就很清楚了:我们首先检查第 0 0 0 个加油站,并试图判断能否环绕一周;如果不能,就从第一个无法到达的加油站开始继续检查。
class Solution {
public:
int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {
int n = gas.size();
int i = 0;
while (i < n) {
int sumOfGas = 0, sumOfCost = 0;
int cnt = 0;
while (cnt < n) {
int j = (i + cnt) % n;
sumOfGas += gas[j];
sumOfCost += cost[j];
if (sumOfCost > sumOfGas) {
break;
}
cnt++;
}
if (cnt == n) {
return i;
} else {
i = i + cnt + 1;
}
}
return -1;
}
};
- 给定一个包含非负整数的数组 nums ,返回其中可以组成三角形三条边的三元组个数。
分析:middle(lc611)
排序+双指针:我们将当 a = nums [ i ] , b = nums [ j ] a = \textit{nums}[i], b = \textit{nums}[j] a=nums[i],b=nums[j] 时,最大的满足 nums [ k ] < nums [ i ] + nums [ j ] \textit{nums}[k] < \textit{nums}[i] + \textit{nums}[j] nums[k]<nums[i]+nums[j] 的下标 k k k 记为 k i , j k_{i, j} ki,j。可以发现,如果我们固定 i i i,那么随着 j j j 的递增,不等式右侧 nums [ i ] + nums [ j ] \textit{nums}[i] + \textit{nums}[j] nums[i]+nums[j] 也是递增的,因此 k i , j k_{i, j} ki,j 也是递增的。
这样一来,我们就可以将 j j j 和 k k k 看成两个同向(递增)移动的指针:
- 我们使用一重循环枚举 i i i。当 i i i 固定时,我们使用双指针同时维护 j j j 和 k k k,它们的初始值均为 i i i;
- 我们每一次将 j j j 向右移动一个位置,即 j ← j + 1 j \leftarrow j+1 j←j+1,并尝试不断向右移动 k k k,使得 k k k 是最大的满足 nums [ k ] < nums [ i ] + nums [ j ] \textit{nums}[k] < \textit{nums}[i] + \textit{nums}[j] nums[k]<nums[i]+nums[j] 的下标。我们将 max ( k − j , 0 ) \max(k - j, 0) max(k−j,0) 累加入答案。
注:若出现不存在满足 nums [ k ] < nums [ i ] + nums [ j ] \textit{nums}[k] < \textit{nums}[i] + \textit{nums}[j] nums[k]<nums[i]+nums[j] 的下标的情况。此时,指针 k k k 不会出现在指针 j j j 的右侧,即 k − j ≤ 0 k - j \leq 0 k−j≤0,因此我们需要将 k − j k−j k−j 与 0 0 0 中的较大值累加入答案,防止错误的负数出现。
当枚举完成后,我们返回累加的答案即可。
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
n = len(nums)
nums.sort()
ans = 0
for i in range(n):
k = i
for j in range(i + 1, n):
while k + 1 < n and nums[k + 1] < nums[i] + nums[j]:
k += 1
ans += max(k - j, 0)
return ans
- :给定一个由0和1组成的矩阵,找出每个元素到最近1的距离,时间复杂度O(n^2)
分析:
动态规划:在dp过程中,遵循着从左上到右下再从右下到左上的方法,原因是,离该点最近的零一定在左上方或者右下方着两个区域内。因此只需要执行两遍即可
vector<vector<int>> updateMatrix(vector<vector<int>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i][j] == 1) {
matrix[i][j] = INT_MAX / 2;
}
}
}
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (i - 1 >= 0) {
matrix[i][j] = min(matrix[i][j], matrix[i - 1][j] + 1);
}
if (j - 1 >= 0) {
matrix[i][j] = min(matrix[i][j], matrix[i][j - 1] + 1);
}
}
}
for (int i = m - 1; i >= 0; --i) {
for (int j = n - 1; j >= 0; --j) {
if (i + 1 < m) {
matrix[i][j] = min(matrix[i][j], matrix[i + 1][j] + 1);
}
if (j + 1 < n) {
matrix[i][j] = min(matrix[i][j], matrix[i][j + 1] + 1);
}
}
}
return matrix;
}
- n堆石子围成一排,每堆石子的量a[i]已知每次可以将相邻两堆合并为一堆,将合并后石子的总量记为这次合并的得分。n-1次合并后石子成为一堆,求这n-1次合并的得分之和的最大值
#include <iostream>
using namespace std;
const int N = 310;
int n;
int a[N], s[N];
int f[N][N];
int main()
{
cin >> n;
for(int i = 1; i <= n; i ++) cin >> a[i];
for(int i = 1; i <= n; i ++) s[i] = s[i - 1] + a[i];
f[0][0] = 0;
for(int len = 2; len <= n; len ++)
for(int i = 1; i + len - 1 <= n; i ++)
{
int j = i + len - 1;
f[i][j] = 2e9;
for(int k = i; k <= j - 1; k ++)
{
f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + s[j] - s[i - 1]);
}
}
cout << f[1][n] << endl;
return 0;
}
- 给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
分析:
法一临时变量:我们可以使用一个临时变量 temp \textit{temp} temp 完成这四项的原地交换:
{ temp = matrix [ row ] [ col ] matrix [ row ] [ col ] = matrix [ n − col − 1 ] [ row ] matrix [ n − col − 1 ] [ row ] = matrix [ n − row − 1 ] [ n − col − 1 ] matrix [ n − row − 1 ] [ n − col − 1 ] = matrix [ col ] [ n − row − 1 ] matrix [ col ] [ n − row − 1 ] = temp \left\{ \begin{alignedat}{2} &\textit{temp} &&= \textit{matrix}[\textit{row}][\textit{col}]\\ &\textit{matrix}[\textit{row}][\textit{col}] &&= \textit{matrix}[n - \textit{col} - 1][\textit{row}]\\ &\textit{matrix}[n - \textit{col} - 1][\textit{row}] &&= \textit{matrix}[n - \textit{row} - 1][n - \textit{col} - 1]\\ &\textit{matrix}[n - \textit{row} - 1][n - \textit{col} - 1] &&= \textit{matrix}[\textit{col}][n - \textit{row} - 1]\\ &\textit{matrix}[\textit{col}][n - \textit{row} - 1] &&= \textit{temp} \end{alignedat} \right. ⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧tempmatrix[row][col]matrix[n−col−1][row]matrix[n−row−1][n−col−1]matrix[col][n−row−1]=matrix[row][col]=matrix[n−col−1][row]=matrix[n−row−1][n−col−1]=matrix[col][n−row−1]=temp
当我们知道了如何原地旋转矩阵之后,还有一个重要的问题在于:我们应该枚举哪些位置 ( row , col ) (\textit{row}, \textit{col}) (row,col) 进行上述的原地交换操作呢?由于每一次原地交换四个位置,因此:
当 n n n 为偶数时,我们需要枚举 n 2 / 4 = ( n / 2 ) × ( n / 2 ) n^2 / 4 = (n/2) \times (n/2) n2/4=(n/2)×(n/2) 个位置,可以将该图形分为四块,以 4 × 4 4 \times 4 4×4 的矩阵为例:
保证了不重复、不遗漏;
当 n n n 为奇数时,由于中心的位置经过旋转后位置不变,我们需要枚举 ( n 2 − 1 ) / 4 = ( ( n − 1 ) / 2 ) × ( ( n + 1 ) / 2 ) (n^2-1) / 4 = ((n-1)/2) \times ((n+1)/2) (n2−1)/4=((n−1)/2)×((n+1)/2) 个位置,需要换一种划分的方式,以 5 × 5 5 \times 5 5×5 的矩阵为例:
法二两次翻转:先将其通过水平轴翻转,再根据主对角线翻转得到答案:
对于水平轴翻转而言,我们只需要枚举矩阵上半部分的元素,和下半部分的元素进行交换,即
matrix [ row ] [ col ] ⇒ 水 平 轴 翻 转 matrix [ n − row − 1 ] [ col ] \textit{matrix}[\textit{row}][\textit{col}] \xRightarrow[]{水平轴翻转} \textit{matrix}[n - \textit{row} - 1][\textit{col}] matrix[row][col]水平轴翻转matrix[n−row−1][col]
对于主对角线翻转而言,我们只需要枚举对角线左侧的元素,和右侧的元素进行交换,即
matrix [ row ] [ col ] ⇒ 主 对 角 线 翻 转 matrix [ col ] [ row ] \textit{matrix}[\textit{row}][\textit{col}] \xRightarrow[]{主对角线翻转} \textit{matrix}[\textit{col}][\textit{row}] matrix[row][col]主对角线翻转matrix[col][row]
将它们联立即可得到:
matrix [ row ] [ col ] ⇒ 水 平 轴 翻 转 matrix [ n − row − 1 ] [ col ] ⇒ 主 对 角 线 翻 转 matrix [ col ] [ n − row − 1 ] \begin{aligned} \textit{matrix}[\textit{row}][\textit{col}] & \xRightarrow[]{水平轴翻转} \textit{matrix}[n - \textit{row} - 1][\textit{col}] \\ &\xRightarrow[]{主对角线翻转} \textit{matrix}[\textit{col}][n - \textit{row} - 1] \end{aligned} matrix[row][col]水平轴翻转matrix[n−row−1][col]主对角线翻转matrix[col][n−row−1]
和方法一、方法二中的关键等式:
matrix new [ col ] [ n − row − 1 ] = matrix [ row ] [ col ] \textit{matrix}_\textit{new}[\textit{col}][n - \textit{row} - 1] = \textit{matrix}[\textit{row}][\textit{col}] matrixnew[col][n−row−1]=matrix[row][col]
是一致的。
class Solution_1 {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < (n + 1) / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
};
class Solution_2 {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
// 水平翻转
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < n; ++j) {
swap(matrix[i][j], matrix[n - i - 1][j]);
}
}
// 主对角线翻转
for (int i = 0; i < n; ++i) {
for (int j = 0; j < i; ++j) {
swap(matrix[i][j], matrix[j][i]);
}
}
}
};
二、八股文
1.重写与重载的区别是什么 (override VS overload)
2. C++的struct和class的区别是什么(1, 2)
3. git rebase和合并多个commit(1, 2, 3)
4. 训练时出现不收敛的情况怎么办,为什么会出现不收敛(1, 2)
5. LR与决策树的区别(1, 2)
三、其他
- 两个桶分别装了一样多的红色和蓝色的颜料。先从蓝色桶里舀一杯倒入红色中,搅拌不均匀。再从有蓝色的红色桶中舀一杯倒入蓝色桶里,问两个桶中蓝:红与红:蓝的大小关系?(1)
待解决 (欢迎评论区或私信解答)
-
给出一个有序数组A和一个常数C,求所有长度为C的子序列中的最大的间距D。
一个数组的间距的定义:所有相邻两个元素中,后一个元素减去前一个元素的差值的最小值. 比如[1,4,6,9]的间距是2.
例子:A:[1,3,6,10], C:3。最大间距D应该是4,对应的一个子序列可以是[1,6,10]。 -
给定一个数字矩阵和一个数字target,比如5。从数字1开始(矩阵中可能有多个1),每次可以向上下左右选择一个方向移动一次,可以移动的条件是下个数字必须是上个数字+1,比如1必须找上下左右为2的点,2必须找上下左右为3的点,以此类推。求到达target一共有几个路径。
-
给定一个只包含0和1的字符串,判断其中有无连续的1。若有,则输出比该串大的无连续1的最小值串。若无,则不做操作
例:给定 ‘11011’ ,则输出 ‘100000’ ;给定 ‘10011’ ,则输出 ‘10100’
(参考:感觉有点像字符串匹配,只要第一次匹配到’011’模式串就改成’100’,然后后面全部置0,仅供参考) -
给定两个字符串 target 和 block,对bolck进行子串选取,选取出的子串可对target进行重构。问最少需要选取多少block子串进行重构。(子串须保持相对顺序,但不要求连续)
例:(1)target = ‘aaa’ ,block = ‘ab’ ,输出为3。即分别选取block子串中的 ‘a’、 ‘a’、 ‘a’;(2)target = ‘abcd’ ,block = ‘bcad’ ,输出为2。即分别选取子串 ‘a’、‘bcd’
(参考:双指针 i,j 分别遍历target和block,j会回溯。时间复杂度是len(target)*len(block)) -
给你一个数组和一个整数k,从数组中选择两个长度为k的子数组(这两个子数组不允许有交叉),使得这两个子数组的数组和,相加之后最大。要求时间复杂度O(n)O(n)
输入:[8,8,9,9,10,10,9,8,7], k=3
输出:5,[[8,9,9],[10,10,9]]
(参考:可以借助一个辅助数组,下标i表示从i能取到的长度为k的子数组和的最大值)