LeetCode 71. 简化路径(华为2020.9秋招面试第2题)
分析
先对path路径按'/'
分隔, 进行扫描,
当不是'/'
的时候加入到当前扫描的的字符串name
中
如果是'/'
, 那么需要分情况加入到答案数组res
中,
- 如果前面扫描出来的当前
name == ".."
, 那么需要将res
最后一个'\'
前的目录连带'\'
弹出 - 如果扫描出的来
name
是个目录, 并且name
不是 原地不动'.'
并且 目录不是\\
,\\
会造成name
为空, 那么res += name
此时操作完, 需要将name
清空.
code
class Solution {
public:
string simplifyPath(string path) {
string res, name; // res 表示答案, name表示当前取到的目录名
// 方向 ->
if (path.back() != '/') path += '/';
for (auto c : path){
if (c != '/') name += c;
else {
if (name == ".."){
while (res.size() && res.back() != '/') res.pop_back();//将当前目录的文件名去掉
if (res.size()) res.pop_back(); //将文件名前面的/去掉
}else if (name != "." && name != "") {
//name != ""是因为会出现//这种情况 //字符的话 name会为“”, 直接不处理即可, “.”也同样不处理
res += '/' + name; // 追加正确的目录'/' + name
}
name.clear();// 因为else 这个分支处理完了name, 所以需要清空
}
}
if (res.empty()) res = "/";
return res;
}
};
LeetCode 72. 编辑距离
分析
经典dp模版题
f[i][j] 表示s1前i个字符 和 s2前j个字符匹配最少的操作步数
删:s1删除一个元素,使得s1[1 ~ i]和s2[1 ~ j]匹配, 那么在删除操作之前 s1[1 ~ i - 1] 已经和s2[1 ~ j]匹配, 因此前一步是f[i - 1][j]
增 :s1增加一个元素, 使得s1[1 ~ i]和s2[1 ~ j]匹配, 在s1[i]后增加一个元素匹配s2[1 ~ j], 那么增加的元素必定是s2[j], 那么在增加元素之前, s1[1 ~ i]只能匹配s2[1 ~ j - 1], 因此前一步是f[i][j - 1]
改: s1修改一个元素, 使得s1[1 ~ i]和s2[1 ~ j]匹配. 那么修改之前, s1[1 ~ i - 1] 匹配s2[1 ~ j - 1], , 因此前一步是f[i - 1][j - 1]
综上, 如果s1[i] != s2[j]
, 那么f[i][j] = min(f[i - 1][j], min(f[i][j - 1], f[i - 1][j - 1])) + 1
code
class Solution {
public:
int minDistance(string word1, string word2) {
int n = word1.size(), m = word2.size();
word1 = ' ' + word1, word2 = ' ' + word2;
vector<vector<int>> f(n + 1, vector<int>(m + 1));
for (int i = 1; i <= n; i ++ ) f[i][0] = i;
for (int i = 1; i <= m; i ++ ) f[0][i] = i;
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= m; j ++ )
if (word1[i] == word2[j]) f[i][j] = f[i - 1][j - 1];
else f[i][j] = min(f[i - 1][j], min(f[i][j - 1], f[i - 1][j - 1])) + 1;
return f[n][m];
}
};
LeetCode 73. 矩阵置零
分析
原地算法, 分别用矩阵的当前行, 记录当前行每个元素所在的列是否存在0,
第0行, 第0列额外用r0, c0标记
code
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
if (matrix.empty() || matrix[0].empty()) return ;
int n = matrix.size(), m = matrix[0].size();
int r0 = 1, c0 = 1;
for (int i = 0; i < m; i ++ ) if (!matrix[0][i]) r0 = 0;
for (int i = 0; i < n; i ++ ) if (!matrix[i][0]) c0 = 0;
for (int i = 1; i < m; i ++ )
for (int j = 1; j < n; j ++ )
if (!matrix[j][i]) matrix[0][i] = 0;
for (int i = 1; i < n; i ++ )
for (int j = 1; j < m; j ++ )
if (!matrix[i][j]) matrix[i][0] = 0;
for (int i = 1; i < m; i ++ )
if (!matrix[0][i])
for (int j = 1; j < n; j ++ )
matrix[j][i] = 0;
for (int i = 1; i < n; i ++ )
if (!matrix[i][0])
for (int j = 1; j < m; j ++ )
matrix[i][j] = 0;
if (!r0) for (int i = 0; i < m; i ++ ) matrix[0][i] = 0;
if (!c0) for (int i = 0; i < n; i ++ ) matrix[i][0] = 0;
}
};
LeetCode 74. 搜索二维矩阵
分析
转为1维去搜索, 1维转2维 x坐标 / m, y坐标 %m.
code
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int n = matrix.size(), m = matrix[0].size();
int l = 0, r = n * m - 1;
while (l < r){
int mid = l + r >> 1;
if (matrix[mid / m][mid % m] >= target) r = mid;
else l = mid + 1;
}
return matrix[l / m][l % m] == target;
}
};
LeetCode 75. 颜色分类 (2020.8 百度提前批面试题)
分析
code
class Solution {
public:
void sortColors(vector<int>& a) {
for (int i = 0, j = 0, k = a.size() - 1; i < k;){
if (a[i] == 0) swap(a[i ++], a[j ++ ]);
else if (a[i] == 2) swap(a[i], a[k -- ]);
else i ++;
}
}
};
LeetCode 76. 最小覆盖子串
分析
枚举每个终点i
, 找到离i
最近的距离j
, 使得[j, i]之间包含t的所有字符
因为用的是双指针, 必须保证i
往后移动, j
不会往前走
证明:
如果i
往后移动变成i'
, j
不可能往前走到j'
, 因为[j,i]已经包含t的所有字符,因此[j, i’]也包含t的所有字符, 题目要求最短的包含t的长度, 因此j'
至少得在j
的位置
如何统计[j,i]中t中包含的字符数量, 用cnt
表示有效(窗口中出现, t内也出现, 且窗口中字符个数不超过t中对应的字符个数)的字符个数(???, 说人话)
如下图:
t中如果有4个a
, 如果当前窗口已经有4个a
, 再加入1个a
, 那么新加的这个a
, 是无效的a
, 因此有效字符个数cnt
不变
反之, 如果t内需要有5个a
, 窗口只有3个a
, 再加1个a
, 此时的a
是有效的,
cnt ++
问题3 :
什么时候j
可以往后移动?
ans : 如果窗口中a[j]
出现的次数 > t内包含的a[j]
的次数, 说明a[j]
多余, 可以往后移动;反之, a[j]
出现的次数 <= t内包含的a[j]
的次数, 则不能往后移动, 因此往后移动a[j]
的次数就少了, 不能包含t内所有字符了.
联动 leetcode3(同样的方法)
code
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> hs, ht;
for (auto c : t) ht[c] ++;
int cnt = 0;
string res;
for (int i = 0, j = 0; i < s.size(); i ++ ){
hs[s[i]] ++;
if (hs[s[i]] <= ht[s[i]]) cnt ++;
while (hs[s[j]] > ht[s[j]]) hs[s[j ++ ]] --;
if (cnt == t.size())
if (res.empty() || i - j + 1 < res.size())
res = s.substr(j, i - j + 1);
}
return res;
}
};
LeetCode 77. 组合
分析
递归树如图
注意递归的结束条件 : path.size() == k
code
class Solution {
public:
int _n;
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> combine(int n, int k) {
_n = n;
dfs(1, k);
return res;
}
void dfs(int u, int k){
if (path.size() == k){
res.push_back(path);
return ;
}
for (int i = u; i <= _n; i ++ ){
path.push_back(i);
dfs(i + 1, k);
path.pop_back();
}
}
};
LeetCode 78. 子集
分析
原理同上一题
code
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> subsets(vector<int>& nums) {
for (int i = 0; i <= nums.size(); i ++ )
dfs(nums, 0, i);
return res;
}
void dfs(vector<int>& nums, int u, int k){
if (path.size() == k){
res.push_back(path);
return ;
}
for (int i = u; i < nums.size(); i ++ ){
path.push_back(nums[i]);
dfs(nums, i + 1, k);
path.pop_back();
}
}
};
LeetCode 79. 单词搜索
分析
枚举每个起点递归搜索, 如果一个起点, 能够搜到, 直接返回true
否则, 返回false
code
class Solution {
public:
int dx[4] = {
-1, 0, 1, 0}, dy[4] = {
0, 1, 0, -1};
bool exist(vector<vector<char>>& board, string word) {
int n = board.size(), m = board[0].size();
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
if (dfs(board, word, 0, i, j)) return true;
return false;
}
bool dfs(vector<vector<char>>& board, string word, int u, int x, int y){
if (board[x][y] != word[u]) return false;
if (u == word.size() - 1) return true; // 此时当前的board[x][y] == word[u], 且u是word.size() - 1了
char t = board[x][y];
board[x][y] = '.'; // 防止走回头路
for (int i = 0; i < 4; i ++ ){
int a = x + dx[i], b = y + dy[i];
if (a < 0 || a >= board.size() || b < 0 || b >= board[0].size() || board[a][b] == '.') continue;
if (dfs(board, word, u + 1, a, b)) return true;
}
board[x][y] = t;
return false;
}
};
LeetCode 80. 删除排序数组中的重复项 II
分析
双指针算法
i
表示当前扫描扫第几个数, k
表示当前的有效数字
- 每次怎么判断
a[i]
是否有效?
ans : 在有效的[0, k - 1]判断a[i]
出现了几次, 如果a[i]
出现的次数 < 2, 那么a[i]
是有效的, 可以放到有效的[0, k]内, 如果a[i]
出现的次数 >= 2, 那么直接跳过, 因为此时a[i]
再放进去, 就超过2个相同数字了 - 怎么判断[0, k - 1]
a[i]
有几个?
ans : 总的序列有序, 并且因为有效的[0, k - 1]也是有序的,a[i]
是当前枚举到的最大一个数, 如果[0, k]内有a[i]
的话, 一定是在最后的位置附近. 所以只需要判断下, [0, k - 1]最后2个位置 是不是a[i]
即可. 如果后2个是a[i]
, 那么可以跳过, 如果有一个不是a[i]
, 说明a[i]
是有效的.
因此整个数组扫描一遍 O ( n ) O(n) O(n).
联动leetcode 26
code
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
int k = 0;
for (auto x : nums)
if (k < 2 || (nums[k - 1] != x || nums[k - 2] != x))
nums[k ++] = x;
return k;
}
};