字节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;
}

T4 二叉树剪枝

posted @ 2025-04-25 21:20  Sylvia_lee  阅读(31)  评论(0)    收藏  举报