字节25飞书训练营笔试0421:字符串 栈 回溯 二叉树 dfs
T1 字符串提取
Problem:
输入一个字符串,输出所有被(和)包裹的字符串列表,注意使用acm模式输入输出
例1
输入:'a*(b+c*(d-e))'
输出:[ "b+c*(d-e)", "d-e" ]
例2
输入: "(()"
输出: []
Solution:
Tag:栈 字符串
复杂度:时间 O(n) 空间 O(n)
思路:将字符串从前往后扫,使用栈记录左括号位置i,遇到右括号j的时候判断栈是不是空的,不是空的就把栈顶的左括号位置弹出,并且将i+1 - j-1的子串记录下来。
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
vector<string> extractSubstrings(const string& s) {
vector<string> res;
stack<int> st; // 记录左括号位置
for (int i = 0; i < s.size(); ++i) {
if (s[i] == '(') {
st.push(i);
} else if (s[i] == ')') {
if (!st.empty()) {
int start = st.top();
st.pop();
res.push_back(s.substr(start + 1, i - start - 1));
}
}
}
return res;
}
int main() {
string input;
getline(cin, input); // 读取整行输入
vector<string> result = extractSubstrings(input);
// 按题目要求格式输出
cout << "[";
for (size_t i = 0; i < result.size(); ++i) {
if (i == 0) cout << " ";
cout << "\"" << result[i] << "\"";
if (i != result.size() - 1) cout << ", ";
if (i == result.size() - 1) cout << " ";
}
cout << "]" << endl;
return 0;
}
T2 39. 组合总和
Problem:
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合,请你输出组合数量。
注意使用acm模式输入输出
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
!!跟leetcode上的略有不同,只要求输出组合数量,lc上要求输出所有组合。
Solution:
Tag:搜索回溯
复杂度:
时间:最坏时间复杂度:O(2^n),每个元素都有选或不选两种可能性(虽然允许重复选择,但通过剪枝优化后实际路径会减少),优化后时间复杂度:O(n^(target/min_candidate)),例如候选数组最小元素为2,目标值为8时,复杂度约为O(n^4)
空间:最大递归深度为 O(target/min_candidate),例如候选数组最小元素是1,目标值为100时,递归深度为100层
思路:递归函数backtrack(candidates,start,target),candidates数组的第start位,还剩target要组合。递归终止的条件是target<=0或者candidates数组被用完,因为每个数字可以无限制被重复选取,所以下一次搜索目标仍是从start开始的,并且排序之后可以进行剪枝。
剪枝条件:
若当前元素值超过target,直接终止循环(因后续元素更大)。
#include <iostream>
#include <vector>
#include <sstream>
#include <algorithm>
using namespace std;
int count = 0;//记录组合总数
void backtrack(const vector<int>& candidates, int start, int target) {//回溯
if (target == 0) {//回溯终止条件:target=0,即组合成功
count++;
return;
}
//if (target < 0) return;
for (int i = start; i < candidates.size(); ++i) {
if (candidates[i] > target) break; // 剪枝:排序后提前终止无效分支
backtrack(candidates, i, target - candidates[i]);//递归
}
}
int combinationSum(vector<int>& candidates, int target) {
sort(candidates.begin(), candidates.end()); // 排序以优化剪枝
backtrack(candidates, 0, target);
return count;
}
// ACM模式输入处理
vector<int> parseInput(const string& line) {
vector<int> res;
stringstream ss(line);//*****重要*****
char ch;
int num;
while (ss >> ch) {//以空格进行切分
if (isdigit(ch)) {//如果是数字
ss.putback(ch);//放回流中
ss >> num;
res.push_back(num);//将以空格放入res数组中
}
}//输入为[20,3,6],处理后num=20、 3 、6,依次放入res中
return res;
}
int main() {
string line;
int target;
// 读取数组行
getline(cin, line); //输入1:[2,3,5] \n 7
vector<int> candidates = parseInput(line);
// 读取目标值
cin >> target;
cout << combinationSum(candidates, target) << endl;
return 0;
}
class Solution {
public:
void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {
if (idx == candidates.size()) {
return;
}
if (target == 0) {
ans.emplace_back(combine);
return;
}
// 直接跳过
dfs(candidates, target, ans, combine, idx + 1);
// 选择当前数
if (target - candidates[idx] >= 0) {
combine.emplace_back(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> combine;
dfs(candidates, target, ans, combine, 0);
return ans;
}
};
T3 LCR 098. 不同路径 && 62. 不同路径
Problem:
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。
问总共有多少条不同的路径?
Solution:
Tag:dp 组合数学
solution1 dp:
复杂度:时间 O(m*n) , 空间 O(m*n)
思路:动态转移方程:dp[i][j] = dp[i-1][j] + dp[i][j-1],dp[i][j]为从0,0走到i,j路径数。
int uniquePaths(int m, int n) {
vector<vector<int>> dp(m, vector<int>(n, 1));//m*n的动态数组,初始化为1
for (int i = 1; i < m; ++i) {
for (int j = 1; j < n; ++j) {
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
}
return dp[m-1][n-1];
}
solution2 组合数学:
复杂度:时间 O(min(n,m)) , 空间 O(1)
思路:路径总数等价于从m+n-2步中选择m-1次向下(或n-1次向右)的组合数,公式如下,约分一下,分子是 (m+n−2)(m+n−3)⋯(max(m,n));分母是 (min(m,n)−1)!,注意边乘边除,避免溢出。
\[C(m+n−2, min(m−1,n−1))=
\frac{(m+n−2)!}{(m−1)!(n−1)!}
\]
int uniquePaths(int m, int n) {
long result = 1;
int total = m + n - 2;
int k = min(m - 1, n - 1);
for (int i = 1; i <= k; i++) {
result = result * (total - k + i) / i;
}
return (int)result;
}

浙公网安备 33010602011771号