目录
- 5月1日:1305. 两棵二叉搜索树中的所有元素
- 5月2日:591. 标签验证器
- 5月3日:937. 重新排列日志文件
- 5月4日:1823. 找出游戏的获胜者
- 5月5日:713. 乘积小于 K 的子数组
- 5月6日:933. 最近的请求次数
- 5月7日:433. 最小基因变化
- 5月8日:442. 数组中重复的数据
- 5月9日:942. 增减字符串匹配
- 5月10日:1728. 猫和老鼠 II(难)
- 5月11日:449. 序列化和反序列化二叉搜索树
- 5月12日:944. 删列造序
- 5月13日:面试题 01.05. 一次编辑
- 5月18日:668. 乘法表中第k小的数 (二分)
- 5月18日:462. 最少移动次数使数组元素相等 II
- 5月19日:436. 寻找右区间
- 5月20日:436. 寻找右区间
- 5月21日:961. 在长度 2N 的数组中找出重复 N 次的元素
- 5月22日:464. 我能赢吗 (状态压缩+DFS)
- 5月23日:675. 为高尔夫比赛砍树
- 5月24日:965. 单值二叉树
- 5月25日:467. 环绕字符串中唯一的子字符串
- 5月26日:699. 掉落的方块
- 5月27日:面试题 17.11. 单词距离
- 5月28日:1021. 删除最外层的括号
- 5月29日:468. 验证IP地址
- 5月30日:1022. 从根到叶的二进制数之和
- 5月31日:剑指 Offer II 114. 外星文字典
5月1日:1305. 两棵二叉搜索树中的所有元素
先遍历,后排序
s Solution {
public:
vector<int> res;
void dfs(TreeNode* root)
{
if (root == nullptr) return;
dfs(root->left);
res.push_back(root->val);
dfs(root->right);
}
vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
dfs(root1);
dfs(root2);
sort(res.begin(), res.end());
return res;
}
};
分别对BST数进行遍历,后归并
class Solution {
public:
void dfs(TreeNode* root, vector<int> &nums)
{
if (root == nullptr) return;
dfs(root->left, nums);
nums.push_back(root->val);
dfs(root->right, nums);
}
vector<int> getAllElements(TreeNode* root1, TreeNode* root2) {
vector<int> nums1, nums2,res;
dfs(root1, nums1);
dfs(root2, nums2);
int i = 0, j = 0;
while (i < nums1.size() && j < nums2.size())
{
if (nums1[i] < nums2[j])
res.push_back(nums1[i ++ ]);
else if (nums1[i] > nums2[j])
res.push_back(nums2[j ++ ]);
else
{
res.push_back(nums1[i ++ ]);
res.push_back(nums2[j ++ ]);
}
}
while (i < nums1.size()) res.push_back(nums1[i ++ ]);
while (j < nums2.size()) res.push_back(nums2[j ++ ]);
return res;
}
};
5月2日:591. 标签验证器
注意这个题的思路,先检查什么,后检查什么!
class Solution {
public:
bool isValid(string code) {
int n = code.size();
stack<string> tags;
int i = 0;
while (i < n) {
if (code[i] == '<') {
if (i == n - 1) return false;
if (code[i + 1] == '/') {
int j = code.find('>', i); // 从code的第i个字符查找'>'
if (j == string::npos) return false; // 未找到
string tagname = code.substr(i + 2, j - (i + 2));
if (tags.empty() || tags.top() != tagname) return false;
tags.pop();
i = j + 1;
if (tags.empty() && i != n)
return false;
} else if (code[i + 1] == '!') {
if (tags.empty()) return false;
string cdata = code.substr(i + 2, 7);
if (cdata != "[CDATA[") return false;
int j = code.find("]]>", i);
if (j == string::npos) return false;
i = j + 1;
} else {
int j = code.find('>', i);
if (j == string::npos) return false;
string tagname = code.substr(i + 1, j - (i + 1));
if (tagname.size() < 1 || tagname.size() > 9) return false;
if (!all_of(tagname.begin(), tagname.end(), // 检查tagname中的字母是否都是大写
[](unsigned char c) { return isupper(c); }))
return false;
tags.push(move(tagname)); // 掏空tagname,并将其值压入栈
i = j + 1;
}
} else {
if (tags.empty()) return false;
i++;
}
}
return tags.empty();
}
};
5月3日:937. 重新排列日志文件
class Solution {
public:
vector<string> reorderLogFiles(vector<string>& logs) {
queue<string> st_nums;
vector<pair<string, string>> mp;
for (auto s : logs)
{
int first_space = s.find(' ', 0);
string str = s.substr(first_space + 1);
bool flag = true;
for (auto c : str)
if (isalpha(c))
{
string tag = s.substr(0, first_space);
mp.push_back({str, tag});
flag = false;
break;
}
if (flag) st_nums.push(s);
}
sort(mp.begin(), mp.end());
vector<string> res;
for (auto s : mp)
res.push_back(s.second + ' ' + s.first);
while (st_nums.size())
{
res.push_back(st_nums.front());
st_nums.pop();
}
return res;
}
};
5月4日:1823. 找出游戏的获胜者
这个题目与ACwing 4400 玩游戏类似,但是这个题目的k
值是不变的,那个题目会改变。那个题目用的是队列模拟来解决。
方法一:队列
class Solution {
public:
int findTheWinner(int n, int k) {
queue<int> q;
for (int i = 1; i <= n; i ++ ) q.push(i);
while (n != 1)
{
for (int i = 0; i < k - 1; i ++ )
{
q.push(q.front());
q.pop();
}
q.pop();
n -- ;
}
return q.front();
}
};
上面的方法当n
很大的时候会TLE
,比如这个题目:剑指 Offer 62. 圆圈中最后剩下的数字
动态规划
思路以及下面评论1
的公式推导。
注意这个题解的返回值下标是从0
开始,而本题要求是从1
开始的。注意理解f[n, m] = (m + x) % n
的推导。
递归写法
class Solution {
public:
int f(int n, int m)
{
if (n == 1) return 0;
int x = f(n - 1, m);
return (m + x) % n;
}
int findTheWinner(int n, int k) {
return f(n, k) + 1;
}
};
迭代写法,避免递归调用栈
class Solution {
public:
int findTheWinner(int n, int k) {
int f = 0;
for (int i = 2; i <= n; i ++ )
f = (f + k) % i; // 注意这里的i
return f + 1;
}
};
5月5日:713. 乘积小于 K 的子数组
滑动窗口思路:枚举滑动窗口的右端点
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
int n = nums.size(), ret = 0;
int prod = 1, i = 0;
for (int j = 0; j < n; j ++ )
{
prod *= nums[j];
while (i <= j && prod >= k)
{
prod /= nums[i];
i ++ ;
}
ret += j - i + 1;
}
return ret;
}
};
5月6日:933. 最近的请求次数
使用队列来维护之前ping
的数据。
class RecentCounter {
private:
queue<int> q;
public:
RecentCounter() {}
int ping(int t) {
while (q.size() && q.front() + 3000 < t) q.pop();
q.push(t);
return q.size();
}
};
5月7日:433. 最小基因变化
BFS
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
unordered_set<string> cnt, visited; // cnt记录bank中的合法序列,visited记录遍历过的序列
char keys[4] = {'A', 'C', 'G', 'T'};
for (auto &w : bank)
cnt.emplace(w);
if (start == end) return 0;
if (!cnt.count(end)) return -1;
queue<string> qu;
qu.emplace(start);
visited.emplace(start);
int step = 1;
while (qu.size())
{
int sz = qu.size();
for (int i = 0; i < sz; i ++ )
{
string curr = qu.front();
qu.pop();
for (int j = 0; j < 8; j ++ ) // 修改当前序列的每个字符
for (int k = 0; k < 4; k ++ )
if (keys[k] != curr[j]) // 要遍历另外三字符
{
string next = curr;
next[j] = keys[k];
if (!visited.count(next) && cnt.count(next)) // 如果next没有被遍历过并且next合法
{
if (next == end)
return step;
qu.emplace(next);
visited.emplace(next);
}
}
}
step ++ ;
}
return -1;
}
};
更高效的代码:学习一下邻接表
class Solution {
public:
int minMutation(string start, string end, vector<string>& bank) {
int m = start.size(), n = bank.size();
vector<vector<int>> adj(n); // 邻接表,用于存储所有可以仅一步变换就到bank中的元素的序列
int endIndex = -1;
for (int i = 0; i < n; i ++ ) // 遍历bank中的每个元素
{
if (end == bank[i])
endIndex = i;
for (int j = i + 1; j < n; j ++ )
{
int mutations = 0;
for (int k = 0; k < m; k ++ )
{
if (bank[i][k] != bank[j][k]) mutations ++ ;
if (mutations > 1) break;
}
if (mutations == 1)
{
adj[i].emplace_back(j);
adj[j].emplace_back(i);
}
}
}
if (endIndex == -1) return -1;
queue<int> qu;
vector<bool> visited(n, false);
int step = 1;
for(int i = 0; i < n; i ++ )
{
int mutations = 0;
for (int k = 0; k < m; k ++ )
{
if (start[k] != bank[i][k]) mutations ++ ;
if (mutations > 1) break;
}
if (mutations == 1)
{
qu.emplace(i);
visited[i] = true;
}
}
while (qu.size())
{
int sz = qu.size();
for (int i = 0; i < sz; i ++ )
{
int curr = qu.front();
qu.pop();
if (curr == endIndex) return step;
for (auto & next : adj[curr])
{
if (visited[next]) continue;
visited[next] = true;
qu.emplace(next);
}
}
step ++ ;
}
return -1;
}
};
5月8日:442. 数组中重复的数据
将元素交换到对应的位置
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n; i ++ )
while (nums[i] != nums[nums[i] - 1])
swap(nums[i], nums[nums[i] - 1]);
vector<int> ans;
for (int i = 0; i < n; i ++ )
if (nums[i] - 1 != i)
ans.push_back(nums[i]);
return ans;
}
};
使用正负号作为标记
class Solution {
public:
vector<int> findDuplicates(vector<int>& nums) {
int n = nums.size();
vector<int> ans;
for (int i = 0; i < n; ++i) {
int x = abs(nums[i]);
if (nums[x - 1] > 0) {
nums[x - 1] = -nums[x - 1];
}
else {
ans.push_back(x);
}
}
return ans;
}
};
5月9日:942. 增减字符串匹配
贪心
class Solution {
public:
vector<int> diStringMatch(string s) {
int n = s.size();
int i = 0, j = n;
vector<int> res;
for (auto c : s)
if (c == 'D') res.push_back(j -- );
else res.push_back(i ++ );
res.push_back(j);
return res;
}
};
5月10日:1728. 猫和老鼠 II(难)
static const int MOUSE_TURN = 0, CAT_TURN = 1;
static const int UNKNOWN = 0, MOUSE_WIN = 1, CAT_WIN = 2;
static const int MAX_MOVES = 1000;
class Solution {
public:
vector<vector<int>> dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int rows, cols;
vector<string> grid;
int catJump, mouseJump;
int food;
int degrees[64][64][2];
int results[64][64][2][2];
bool canMouseWin(vector<string> grid, int catJump, int mouseJump) {
this->rows = grid.size();
this->cols = grid[0].size();
this->grid = grid;
this->catJump = catJump;
this->mouseJump = mouseJump;
int startMouse = -1, startCat = -1;
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
char c = grid[i][j];
if (c == 'M') {
startMouse = getPos(i, j);
} else if (c == 'C') {
startCat = getPos(i, j);
} else if (c == 'F') {
food = getPos(i, j);
}
}
}
int total = rows * cols;
memset(degrees, 0, sizeof(degrees));
memset(results, 0, sizeof(results));
queue<tuple<int, int, int>> qu;
// 计算每个状态的度
for (int mouse = 0; mouse < total; mouse++) {
int mouseRow = mouse / cols, mouseCol = mouse % cols;
if (grid[mouseRow][mouseCol] == '#') {
continue;
}
for (int cat = 0; cat < total; cat++) {
int catRow = cat / cols, catCol = cat % cols;
if (grid[catRow][catCol] == '#') {
continue;
}
degrees[mouse][cat][MOUSE_TURN]++;
degrees[mouse][cat][CAT_TURN]++;
for (auto & dir : dirs) {
for (int row = mouseRow + dir[0], col = mouseCol + dir[1], jump = 1; row >= 0 && row < rows && col >= 0 && col < cols && grid[row][col] != '#' && jump <= mouseJump; row += dir[0], col += dir[1], jump++) {
int nextMouse = getPos(row, col), nextCat = getPos(catRow, catCol);
degrees[nextMouse][nextCat][MOUSE_TURN]++;
}
for (int row = catRow + dir[0], col = catCol + dir[1], jump = 1; row >= 0 && row < rows && col >= 0 && col < cols && grid[row][col] != '#' && jump <= catJump; row += dir[0], col += dir[1], jump++) {
int nextMouse = getPos(mouseRow, mouseCol), nextCat = getPos(row, col);
degrees[nextMouse][nextCat][CAT_TURN]++;
}
}
}
}
// 猫和老鼠在同一个单元格,猫获胜
for (int pos = 0; pos < total; pos++) {
int row = pos / cols, col = pos % cols;
if (grid[row][col] == '#') {
continue;
}
results[pos][pos][MOUSE_TURN][0] = CAT_WIN;
results[pos][pos][MOUSE_TURN][1] = 0;
results[pos][pos][CAT_TURN][0] = CAT_WIN;
results[pos][pos][CAT_TURN][1] = 0;
qu.emplace(pos, pos, MOUSE_TURN);
qu.emplace(pos, pos, CAT_TURN);
}
// 猫和食物在同一个单元格,猫获胜
for (int mouse = 0; mouse < total; mouse++) {
int mouseRow = mouse / cols, mouseCol = mouse % cols;
if (grid[mouseRow][mouseCol] == '#' || mouse == food) {
continue;
}
results[mouse][food][MOUSE_TURN][0] = CAT_WIN;
results[mouse][food][MOUSE_TURN][1] = 0;
results[mouse][food][CAT_TURN][0] = CAT_WIN;
results[mouse][food][CAT_TURN][1] = 0;
qu.emplace(mouse, food, MOUSE_TURN);
qu.emplace(mouse, food, CAT_TURN);
}
// 老鼠和食物在同一个单元格且猫和食物不在同一个单元格,老鼠获胜
for (int cat = 0; cat < total; cat++) {
int catRow = cat / cols, catCol = cat % cols;
if (grid[catRow][catCol] == '#' || cat == food) {
continue;
}
results[food][cat][MOUSE_TURN][0] = MOUSE_WIN;
results[food][cat][MOUSE_TURN][1] = 0;
results[food][cat][CAT_TURN][0] = MOUSE_WIN;
results[food][cat][CAT_TURN][1] = 0;
qu.emplace(food, cat, MOUSE_TURN);
qu.emplace(food, cat, CAT_TURN);
}
// 拓扑排序
while (!qu.empty()) {
auto [mouse, cat, turn] = qu.front();
qu.pop();
int result = results[mouse][cat][turn][0];
int moves = results[mouse][cat][turn][1];
vector<tuple<int, int, int>> prevStates = getPrevStates(mouse, cat, turn);
for (auto [prevMouse, prevCat, prevTurn] : prevStates) {
if (results[prevMouse][prevCat][prevTurn][0] == UNKNOWN) {
bool canWin = (result == MOUSE_WIN && prevTurn == MOUSE_TURN) || (result == CAT_WIN && prevTurn == CAT_TURN);
if (canWin) {
results[prevMouse][prevCat][prevTurn][0] = result;
results[prevMouse][prevCat][prevTurn][1] = moves + 1;
qu.emplace(prevMouse, prevCat, prevTurn);
} else {
degrees[prevMouse][prevCat][prevTurn]--;
if (degrees[prevMouse][prevCat][prevTurn] == 0) {
int loseResult = prevTurn == MOUSE_TURN ? CAT_WIN : MOUSE_WIN;
results[prevMouse][prevCat][prevTurn][0] = loseResult;
results[prevMouse][prevCat][prevTurn][1] = moves + 1;
qu.emplace(prevMouse, prevCat, prevTurn);
}
}
}
}
}
return results[startMouse][startCat][MOUSE_TURN][0] == MOUSE_WIN && results[startMouse][startCat][MOUSE_TURN][1] <= MAX_MOVES;
}
vector<tuple<int, int, int>> getPrevStates(int mouse, int cat, int turn) {
vector<tuple<int, int, int>> prevStates;
int mouseRow = mouse / cols, mouseCol = mouse % cols;
int catRow = cat / cols, catCol = cat % cols;
int prevTurn = turn == MOUSE_TURN ? CAT_TURN : MOUSE_TURN;
int maxJump = prevTurn == MOUSE_TURN ? mouseJump : catJump;
int startRow = prevTurn == MOUSE_TURN ? mouseRow : catRow;
int startCol = prevTurn == MOUSE_TURN ? mouseCol : catCol;
prevStates.emplace_back(mouse, cat, prevTurn);
for (auto & dir : dirs) {
for (int i = startRow + dir[0], j = startCol + dir[1], jump = 1; i >= 0 && i < rows && j >= 0 && j < cols && grid[i][j] != '#' && jump <= maxJump; i += dir[0], j += dir[1], jump++) {
int prevMouseRow = prevTurn == MOUSE_TURN ? i : mouseRow;
int prevMouseCol = prevTurn == MOUSE_TURN ? j : mouseCol;
int prevCatRow = prevTurn == MOUSE_TURN ? catRow : i;
int prevCatCol = prevTurn == MOUSE_TURN ? catCol : j;
int prevMouse = getPos(prevMouseRow, prevMouseCol);
int prevCat = getPos(prevCatRow, prevCatCol);
prevStates.emplace_back(prevMouse, prevCat, prevTurn);
}
}
return prevStates;
}
int getPos(int row, int col) {
return row * cols + col;
}
};
5月11日:449. 序列化和反序列化二叉搜索树
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res;
vector<int> arr;
postOrder(root, arr);
if (arr.size() == 0) return res;
for (int i = 0; i < arr.size() - 1; i ++ )
res.append(to_string(arr[i]) + ",");
res.append(to_string(arr.back()));
return res;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if (data.size() == 0) return nullptr;
vector<string> arr = split(data, ',');
stack<int> st;
for (auto &str : arr)
st.emplace(stoi(str));
return construct(INT_MIN, INT_MAX, st);
}
vector<string> split(const string &str, char dec)
{
int pos = 0, start = 0;
vector<string> res;
while (pos < str.size())
{
while (pos < str.size() && str[pos] == dec)
pos ++ ;
start = pos;
while (pos < str.size() && str[pos] != dec)
pos ++ ;
if (start < str.size())
res.emplace_back(str.substr(start, pos - start));
}
return res;
}
void postOrder(TreeNode *root, vector<int> &arr)
{
if (root == nullptr) return;
postOrder(root->left, arr);
postOrder(root->right, arr);
arr.emplace_back(root->val);
}
TreeNode* construct(int lower, int upper, stack<int>& st)
{
if (st.size() == 0 || st.top() < lower || st.top() > upper)
return nullptr;
int val = st.top();
st.pop();
TreeNode* root = new TreeNode(val);
root->right = construct(val, upper, st);
root->left = construct(lower, val, st);
return root;
}
};
5月12日:944. 删列造序
class Solution {
public:
int minDeletionSize(vector<string>& strs) {
int row = strs.size();
int col = strs[0].size();
int ans = 0;
for (int j = 0; j < col; ++j) {
for (int i = 1; i < row; ++i) {
if (strs[i - 1][j] > strs[i][j]) {
ans++;
break;
}
}
}
return ans;
}
};
5月13日:面试题 01.05. 一次编辑
class Solution {
public:
bool oneEditAway(string first, string second) {
int len1 = first.size(), len2 = second.size();
if (abs(len1 - len2) > 1) return false;
if (len1 < len2) swap(first, second);
for (int i = 0, j = 0, diff = 0; i < first.size(); i ++ , j ++ )
{
if (first[i] != second[j])
{
if ( ++ diff > 1) return false;
if (len1 != len2) j -- ; // 当diff=1的时候,j的位置不变,i跳过当前一个不同的字符
}
}
return true;
}
};
5月18日:668. 乘法表中第k小的数 (二分)
class Solution {
public:
int findKthNumber(int m, int n, int k) {
int left = 1, right = m * n;
while (left < right)
{
int x = left + (right - left) / 2;
int count = x / n * n;
for (int i = x / n + 1; i <= m; i ++ )
count += x / i;
if (count >= k)
right = x;
else
left = x + 1;
}
return left;
}
};
5月18日:462. 最少移动次数使数组元素相等 II
class Solution {
public:
int minMoves2(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size(), res = 0, x = nums[nums.size() / 2];
for (int i = 0; i < n; i ++ )
res += abs(nums[i] - x);
return res;
}
};
5月19日:436. 寻找右区间
class Solution {
public:
vector<int> findRightInterval(vector<vector<int>>& intervals) {
int n = intervals.size();
unordered_map<int, int> startLen;
for (int i = 0; i < n; i ++ )
startLen[intervals[i][0]] = i;
sort(intervals.begin(), intervals.end());
vector<int> res(n);
for(int i = 0; i < n; i ++ )
{
int x = intervals[i][1];
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + (r - l) / 2;
if (intervals[mid][0] >= x) r = mid;
else l = mid + 1;
}
if (intervals[l][0] >= x)
res[startLen[intervals[i][0]]] = startLen[intervals[l][0]];
else res[startLen[intervals[i][0]]] = -1;
}
return res;
}
};
5月20日:436. 寻找右区间
class Solution {
public:
vector<int> findRightInterval(vector<vector<int>>& intervals) {
int n = intervals.size();
unordered_map<int, int> startLen;
for (int i = 0; i < n; i ++ )
startLen[intervals[i][0]] = i;
sort(intervals.begin(), intervals.end());
vector<int> res(n);
for(int i = 0; i < n; i ++ )
{
int x = intervals[i][1];
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + (r - l) / 2;
if (intervals[mid][0] >= x) r = mid;
else l = mid + 1;
}
if (intervals[l][0] >= x)
res[startLen[intervals[i][0]]] = startLen[intervals[l][0]];
else res[startLen[intervals[i][0]]] = -1;
}
return res;
}
};
5月21日:961. 在长度 2N 的数组中找出重复 N 次的元素
class Solution {
public:
int repeatedNTimes(vector<int>& nums) {
unordered_set<int> st;
for (auto c : nums)
{
if (!st.count(c))
st.insert(c);
else
return c;
}
return -1;
}
};
5月22日:464. 我能赢吗 (状态压缩+DFS)
class Solution {
public:
unordered_map<int, bool> memo;
bool canIWin(int maxChoosableInteger, int desiredTotal) {
if ((1 + maxChoosableInteger) * (maxChoosableInteger) / 2 < desiredTotal)
return false;
return dfs(maxChoosableInteger, 0, desiredTotal, 0);
}
bool dfs(int maxChoosableInteger, int usedNumbers, int desiredTotal, int currentTotal)
{
if (!memo.count(usedNumbers))
{
bool res = false;
for (int i = 0; i < maxChoosableInteger; i ++ )
{
if (((usedNumbers >> i) & 1) == 0)
{
if (i + 1 + currentTotal >= desiredTotal)
{
res = true;
break;
}
if (!dfs(maxChoosableInteger, usedNumbers | (1 << i), desiredTotal, currentTotal + i + 1))
{
res = true;
break;
}
}
}
memo[usedNumbers] = res;
}
return memo[usedNumbers];
}
};
5月23日:675. 为高尔夫比赛砍树
class Solution {
public:
int dirs[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int bfs(vector<vector<int>>& forest, int sx, int sy, int tx, int ty) {
if (sx == tx && sy == ty) {
return 0;
}
int row = forest.size();
int col = forest[0].size();
int step = 0;
queue<pair<int, int>> qu;
vector<vector<bool>> visited(row, vector<bool>(col, false));
qu.emplace(sx, sy);
visited[sx][sy] = true;
while (!qu.empty()) {
step++;
int sz = qu.size();
for (int i = 0; i < sz; ++i) {
auto [cx, cy] = qu.front();
qu.pop();
for (int j = 0; j < 4; ++j) {
int nx = cx + dirs[j][0];
int ny = cy + dirs[j][1];
if (nx >= 0 && nx < row && ny >= 0 && ny < col) {
if (!visited[nx][ny] && forest[nx][ny] > 0) {
if (nx == tx && ny == ty) {
return step;
}
qu.emplace(nx, ny);
visited[nx][ny] = true;
}
}
}
}
}
return -1;
}
int cutOffTree(vector<vector<int>>& forest) {
vector<pair<int, int>> trees;
int row = forest.size();
int col = forest[0].size();
for (int i = 0; i < row; ++i) {
for (int j = 0; j < col; ++j) {
if (forest[i][j] > 1) {
trees.emplace_back(i, j);
}
}
}
sort(trees.begin(), trees.end(), [&](const pair<int, int> & a, const pair<int, int> & b) {
return forest[a.first][a.second] < forest[b.first][b.second];
});
int cx = 0;
int cy = 0;
int ans = 0;
for (int i = 0; i < trees.size(); ++i) {
int steps = bfs(forest, cx, cy, trees[i].first, trees[i].second);
if (steps == -1) {
return -1;
}
ans += steps;
cx = trees[i].first;
cy = trees[i].second;
}
return ans;
}
};
5月24日:965. 单值二叉树
class Solution {
public:
bool isUnivalTree(TreeNode* root) {
if (!root) return true;
if (root->left)
if (root->val != root->left->val || !isUnivalTree(root->left))
return false;
if (root->right)
if (root->val != root->right->val || !isUnivalTree(root->right))
return false;
return true;
}
};
5月25日:467. 环绕字符串中唯一的子字符串
class Solution {
public:
int findSubstringInWraproundString(string p) {
vector<int> dp(26);
int k = 0;
for (int i = 0; i < p.length(); ++i) {
if (i && (p[i] - p[i - 1] + 26) % 26 == 1) { // 字符之差为 1 或 -25
++k;
} else {
k = 1;
}
dp[p[i] - 'a'] = max(dp[p[i] - 'a'], k);
}
return accumulate(dp.begin(), dp.end(), 0);
}
};
5月26日:699. 掉落的方块
class Solution {
public:
vector<int> fallingSquares(vector<vector<int>>& positions) {
int n = positions.size();
vector<int> heights(n);
for (int i = 0; i < n; i++) {
int left1 = positions[i][0], right1 = positions[i][0] + positions[i][1] - 1;
heights[i] = positions[i][1];
for (int j = 0; j < i; j++) {
int left2 = positions[j][0], right2 = positions[j][0] + positions[j][1] - 1;
if (right1 >= left2 && right2 >= left1) {
heights[i] = max(heights[i], heights[j] + positions[i][1]);
}
}
}
for (int i = 1; i < n; i++) {
heights[i] = max(heights[i], heights[i - 1]);
}
return heights;
}
};
5月27日:面试题 17.11. 单词距离
class Solution {
public:
int findClosest(vector<string>& words, string word1, string word2) {
int minv = words.size();
int a = -1, b = -1;
for (int i = 0; i < words.size(); i ++ )
{
if (words[i] == word1)
a = i;
if (words[i] == word2)
b = i;
if (a >= 0 && b >= 0)
minv = min(minv, abs(a - b));
}
return minv;
}
};
5月28日:1021. 删除最外层的括号
// 计数
class Solution {
public:
string removeOuterParentheses(string s) {
int level = 0;
string res;
for (auto c : s)
{
if (c == ')') level -- ;
if (level) res.push_back(c);
if (c == '(') level ++ ;
}
return res;
}
};
// 栈
class Solution {
public:
string removeOuterParentheses(string s) {
stack<char> st;
string res;
for (auto c : s)
{
if (c == ')') st.pop();
if (!st.empty()) res.push_back(c);
if (c == '(') st.push(c);
}
return res;
}
};
5月29日:468. 验证IP地址
class Solution {
public:
bool is_IPV4(string s)
{
int cnt = 0;
for (int i = 0; i < s.size(); )
{
int j = i;
while (j < s.size() && s[j] != '.') j ++ ;
if (j == s.size() - 1 && s[j] == '.') return false;
string str = s.substr(i, j - i);
cnt ++ ;
if (str.size() > 1 && str[0] == '0') return false;
if (!str.size() || str.size() > 3) return false;
for (int i = 0; i < str.size(); i ++ )
if (!isdigit(str[i])) return false;
if (atoi(str.c_str()) > 255) return false;
i = j + 1;
}
if (cnt != 4) return false;
return true;
}
bool is_IPV6(string s)
{
int cnt = 0;
for (int i = 0; i < s.size(); )
{
int j = i;
while (j < s.size() && s[j] != ':') j ++ ;
if (j == s.size() - 1 && s[j] == ':') return false;
string str = s.substr(i, j - i);
cnt ++ ;
if (str.size() > 4 || str.size() < 1) return false;
for (int k = 0; k < str.size(); k ++ )
if (!isdigit(str[k]))
if (str[k] < 'A' || str[k] > 'F' && str[k] < 'a' || str[k] > 'f')
return false;
i = j + 1;
}
if (cnt != 8) return false;
return true;
}
string validIPAddress(string queryIP) {
bool flag = true;
for (int i = 0; i < queryIP.size(); i ++ )
if (queryIP[i] == '.')
{
flag = false;
break;
}
if (!flag)
{
if (is_IPV4(queryIP))
return "IPv4";
}
else
{
if (is_IPV6(queryIP))
return "IPv6";
}
return "Neither";
}
};
5月30日:1022. 从根到叶的二进制数之和
递归:
class Solution {
public:
int sum = 0;
int res = 0;
void dfs(TreeNode* root)
{
if (root == nullptr) return;
sum = sum * 2 + root->val;
if (root->left == nullptr && root->right == nullptr)
res += sum;
dfs(root->left);
dfs(root->right);
sum = (sum - root->val) / 2;
}
int sumRootToLeaf(TreeNode* root) {
dfs(root);
return res;
}
};
迭代:
class Solution {
public:
int sumRootToLeaf(TreeNode* root) {
stack<TreeNode*> st;
int val = 0, ret = 0;
TreeNode* prev = nullptr;
while (root != nullptr || !st.empty())
{
while (root != nullptr)
{
val = (val << 1) | root->val;
st.push(root);
root = root->left;
}
root = st.top();
if (root->right == nullptr || root->right == prev)
{
if (root->left == nullptr && root->right == nullptr)
ret += val;
val >>= 1;
st.pop();
prev = root;
root = nullptr;
}
else
root = root->right;
}
return ret;
}
};
5月31日:剑指 Offer II 114. 外星文字典
class Solution {
public:
unordered_map<char, vector<char>> edges; // 每个点:对应的边
unordered_map<char, int> indegrees; // 点的入度
bool valid = true;
string alienOrder(vector<string>& words) {
int length = words.size();
// 建立点
for (auto word : words)
{
int wordLength = word.size();
for (int j = 0; j < wordLength; j ++ )
{
char c = word[j];
if (!edges.count(c))
edges[c] = vector<char>();
}
}
// 建立边
for (int i = 1; i < length && valid; i ++ )
addEdge(words[i - 1], words[i]);
if (!valid) return "";
// topsort
queue<char> qu;
// 插入入度为0的点
for (auto c : edges)
if (!indegrees.count(c.first))
qu.push(c.first);
string order;
while (!qu.empty())
{
char u = qu.front(); qu.pop();
order.push_back(u);
for (char v : edges[u])
{
indegrees[v] -- ;
if (indegrees[v] == 0)
qu.push(v);
}
}
return order.size() == edges.size() ? order : "";
}
// 建图
void addEdge(string before, string after)
{
int length1 = before.size(), length2 = after.size();
int length = min(length1, length2);
int index = 0;
while (index < length)
{
char c1 = before[index], c2 = after[index];
if (c1 != c2)
{
edges[c1].push_back(c2);
indegrees[c2] ++ ;
break;
}
index ++ ;
}
if (index == length && length1 > length2)
valid = false;
}
};