一、1089. Duplicate Zeros
给你一个长度固定的整数数组 arr
,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。
注意:请不要在超过该数组长度的位置写入元素。
要求:请对输入的数组 就地 进行上述修改,不要从函数返回任何东西
一道难度easy的题,要通过测试很简单,但是一般人都是直接写个空间复杂度为O(n)的算法,比如我。
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
vector<int> ans;
int n=arr.size(),i=0;
while(ans.size()<n&&i<n)
{
if(arr[i])
ans.push_back(arr[i]);
else
{
ans.push_back(0);
if(ans.size()<n)
ans.push_back(0);
}
++i;
}
arr=ans;
}
};
但大神总是能快速地提出高效的解法:空间复杂度O(1):
1、首先计数数组中有多少个零以决定数组一共需要右移几次;
2、右移后超过原数组长度范围的元素不再考虑;
class Solution {
public:
void duplicateZeros(vector<int>& arr) {
int n=arr.size(),i=0,shift=0;
while(i+shift<n)
shift+=(!arr[i++]);
//将元素右移的过程
for(i=i-1;shift;--i)
{
if(i+shift<n)//一定要有这个判断,因为上一个循环可能是从i+shift==n+1处退出的
arr[i+shift]=arr[i];
if(!arr[i])
--shift,arr[i+shift]=0;
}
}
};
二、1090. Largest Values From Labels
我们有一个项的集合,其中第 i
项的值为 values[i]
,标签为 labels[i]
。
我们从这些项中选出一个子集 S
,这样一来:
|S| <= num_wanted
- 对于任意的标签
L
,子集S
中标签为L
的项的数目总满足<= use_limit
。
返回子集 S
的最大可能的 和
思路:做这题的时候一开始我不是很理解题意,看了中文翻译才明白过来(看了discuss区才发现很多人都看不懂题意,英文原题描述得比较模糊)。直接将values从大到小排序然后依次取出可取的元素相加即可。
class Solution {
public:
int largestValsFromLabels(vector<int>& values, vector<int>& labels, int num_wanted, int use_limit)
{
int n=values.size();
vector<vector<int>> v;
unordered_map<int,int> record;
//每个值对应一个标签,这个是不可变的
for(int i=0;i<n;++i)
v.push_back({values[i],labels[i]});
//排序调用的函数,只对value进行从大到小的排序
auto cmp=[](vector<int>& a,vector<int>& b){
return a[0]>b[0];
};
sort(v.begin(),v.end(),cmp);
int cnt=0,ans=0;
for(int i=0;i<n&&cnt<num_wanted;++i)
{
int value=v[i][0],label=v[i][1];
if(record[label]<use_limit&&cnt<num_wanted)
{
ans+=value,++cnt,++record[label];
}
}
return ans;
}
};
三、1091. Shortest Path in Binary Matrix
在一个 N × N 的方形网格中,每个单元格有两种状态:空(0)或者阻塞(1)。
一条从左上角到右下角、长度为 k
的畅通路径,由满足下述条件的单元格 C_1, C_2, ..., C_k
组成:
- 相邻单元格
C_i
和C_{i+1}
在八个方向之一上连通(此时,C_i
和C_{i+1}
不同且共享边或角) C_1
位于(0, 0)
(即,值为grid[0][0]
)C_k
位于(N-1, N-1)
(即,值为grid[N-1][N-1]
)- 如果
C_i
位于(r, c)
,则grid[r][c]
为空(即,grid[r][c] == 0
)
返回这条从左上角到右下角的最短畅通路径的长度。如果不存在这样的路径,返回 -1
思路:这是一道典型的求最短路的问题,这种问题我总是优先考虑是否能用BFS解,往往是可以的。需要注意的是离开时的距离就已经是1。
class Solution {
public:
int shortestPathBinaryMatrix(vector<vector<int>>& grid) {
if(grid[0][0])
return -1;
int rows=grid.size(),cols=grid[0].size();
queue<vector<int>> q;
//队列元素为:行、列、已经走过的距离
q.push({0,0,1});
int dir[8][2]={{-1,0},{1,0},{0,-1},{0,1},{-1,-1},{-1,1},{1,-1},{1,1}};
//(row,col)一旦入队即表明该点已经走过,将对应的grid修改为1确保不再走。
grid[0][0]=1;
while(!q.empty())
{
int n=q.size(),row,col,dis;
while(n--)
{
vector<int> cur=q.front();q.pop();
row=cur[0],col=cur[1],dis=cur[2];
if(row==rows-1&&col==cols-1)
return dis;
for(int i=0;i<8;++i)
{
int nextr=row+dir[i][0],nextc=col+dir[i][1];
if(0<=nextr&&nextr<rows&&0<=nextc&&nextc<cols&&!grid[nextr][nextc])
q.push({nextr,nextc,dis+1}),grid[nextr][nextc]=1;
}
}
}
return -1;
}
};
四、1092. Shortest Common Supersequence
给出两个字符串 str1
和 str2
,返回同时以 str1
和 str2
作为子序列的最短字符串。如果答案不止一个,则可以返回满足条件的任意一个答案。
(如果从字符串 T 中删除一些字符(也可能不删除,并且选出的这些字符可以位于 T 中的 任意位置),可以得到字符串 S,那么 S 就是 T 的子序列)
思路:这道题不难想到要先求出两个字符串的最长公共子序列,再由该子序列构造出答案。但在规定时间内我没有构造出来,看了discuss区域大神的解法也发现了我之间求子序列的代码写得不够简洁,因此根据大神的思路重写了一遍代码。
class Solution {
public:
string shortestCommonSupersequence(string str1, string str2) {
int n=str1.size(),m=str2.size();
vector<vector<string>> dp(n+1,vector<string>(m+1));
//直接求出最长公共子列
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
{
if(str1[i-1]==str2[j-1])
dp[i][j]=dp[i-1][j-1]+str1[i-1];
else
dp[i][j]=(dp[i-1][j].size()>dp[i][j-1].size()?dp[i-1][j]:dp[i][j-1]);
}
string ans;
int p1=0,p2=0;
for(char ch:dp[n][m])
{
while(p1<n&&str1[p1]!=ch)
ans+=str1[p1++];
while(p2<m&&str2[p2]!=ch)
ans+=str2[p2++];
ans+=ch,++p1,++p2;
}
//此时str1和str2可能还剩结尾一部分不属于公共序列的子串
return ans+str1.substr(p1)+str2.substr(p2);
}
};