题解 | #未排序数组中累加和为给定值的最长子数组长度#

字符串的排列

http://www.nowcoder.com/practice/fe6b651b66ae47d7acce78ffdd9a96c7

解法一:回溯 + set

利用回溯方法进行求解的思路如下:(具体递归过程见图示)

  1. 对于每一个位置,都有「选」和「不选」两种可能,因此需要定义一个state数组用来记录「当前元素是否已经被选择」;
  2. 字符串cur用来记录「当前」字符串的情况,在每次递归时,在cur末尾加上当前字符(若其未被选择过),然后进入下一次递归;当cur字符串与原字符串长度相同时,即说明已经完成「整个字符串的排列」,因此将其添加至结果中;
  3. 此外,在每次递归时,需要将该字符的state数组置位true,说明其已经被选择了;在递归完成后,需要「恢复现场」:把该字符的state数组置位false,并将其从cur中去掉;
  4. 每次递归过程都从0开始(与这题不同),这是因为:尽管已经递归到后面的元素了,但前面的元素仍然需要有「被选择」的机会,例如:在字符串”abc“中,当字符c被加入到cur中时,字符a和字符b仍然需要有「被选择」的机会,因此每次递归都需要从0位置开始。
  5. 由于原字符串会出现重复字符,因此需要用set数据结构完成去重工作。

图片说明

基于上述思路,实现的代码如下:

class Solution {
public:
    set<string> res; 
    vector<string> Permutation(string str) {
        if (str.empty()) 
            return vector<string>(res.begin(), res.end()); 
          sort(str.begin(), str.end()); // 排序
        vector<bool> state(str.size(), false); // 定义state数组,原来表明是否已经被选择过
        backtracking(str, "", state); // 调用递归函数
        return vector<string>(res.begin(), res.end());  
    }
    void backtracking(string s, string cur, vector<bool> &state) {
        if (cur.size() == s.size()) { // 遍历到末尾
            res.insert(cur); // 存储结果
            return; 
        }
        for (int i = 0; i < s.size(); i ++) { //每一次递归都从0位置开始遍历 
            if (state[i]) // 已经被使用了,跳过
                continue; 
            state[i] = true; // 置为true,代表使用它
            cur += s[i]; // 加到cur中
            backtracking(s, cur, state); // 下一次递归
            state[i] = false; // 恢复现场
            cur = cur.substr(0, cur.length() - 1); // 去掉最后一个字符
        }
    }
};

设数组的长度为N,则(最坏情况下,即无重复)全排列共有N!种,因此共需要调用递归函数次。此外,在每次递归时,set(底层实现为红黑树)插入数据的平均时间复杂度为,但每次递归需要完成for循环(最坏时间复杂度为),且保存结果需要时间复杂度,而,故总时间复杂度为

该方法递归使用栈空间,空间复杂度为

解法二:回溯 + 不需要set

解法一使用set进行去重,但其在去重前会获得许多重复的结果。这一解法可以进一步优化:在递归的过程中即完成去重的工作。

思路如下:

在进行每次递归时,如果当前元素与前一元素相同,且前一元素已经被使用过了,则无需再对当前元素进行递归,具体过程见图示。
(为更好地表示重复元素,图中将两个b字符分别表示为b1、b2)

图片说明

实现的代码如下:

class Solution {
public:
    vector<string> res; 
    vector<string> Permutation(string str) {
        if (str.empty()) 
            return res; 
        sort(str.begin(), str.end()); 
        vector<bool> state(str.size(), false); // 定义state数组,原来表明是否已经被选择过
        backtracking(str, "", state); // 调用递归函数
        return res;  
    }
    void backtracking(string s, string cur, vector<bool> &state) {
        if (cur.size() == s.size()) { // 遍历到末尾
            res.push_back(cur); // 存储结果
            return; 
        }
        for (int i = 0; i < s.size(); i ++) { //每一次递归都从0位置开始遍历 
            if (state[i] || (i > 0 && s[i] == s[i - 1] && state[i - 1])) // 已经被使用了,跳过;或是遇到重复,并且前一个重复元素已经被使用过了,跳过
                continue; 
            state[i] = true; // 置为true,代表使用它
            cur += s[i]; // 加到cur中
            backtracking(s, cur, state); // 下一次递归
            state[i] = false; // 恢复现场
            cur = cur.substr(0, cur.length() - 1); // 去掉最后一个字符
        }
    }
};

该方法在最坏情况下时间复杂度与解法一相同,为,但当出现重复时,解法二不需要「求出所有的重复组合之后再去重」,因此相对于解法一而言,优化了时间复杂度;

与解法一同理,该方法空间复杂度为

全部评论

相关推荐

评论
3
收藏
分享

创作者周榜

更多
正在热议
更多
# 长得好看会提高面试通过率吗? #
4471次浏览 48人参与
# 离家近房租贵VS离家远但房租低,怎么选 #
16917次浏览 137人参与
# 巨人网络春招 #
11543次浏览 228人参与
# 沪漂/北漂你觉得哪个更苦? #
1616次浏览 41人参与
# 你的实习产出是真实的还是包装的? #
3230次浏览 54人参与
# 春招至今,你的战绩如何? #
16140次浏览 146人参与
# MiniMax求职进展汇总 #
25260次浏览 322人参与
# HR最不可信的一句话是__ #
1107次浏览 32人参与
# AI面会问哪些问题? #
971次浏览 24人参与
# 你做过最难的笔试是哪家公司 #
1306次浏览 23人参与
# AI时代,哪个岗位还有“活路” #
2930次浏览 53人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152945次浏览 889人参与
# 简历第一个项目做什么 #
32180次浏览 363人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
8029次浏览 43人参与
# XX请雇我工作 #
51164次浏览 171人参与
# 简历中的项目经历要怎么写? #
311119次浏览 4271人参与
# 投格力的你,拿到offer了吗? #
178382次浏览 891人参与
# 你最满意的offer薪资是哪家公司? #
77008次浏览 375人参与
# AI时代,哪些岗位最容易被淘汰 #
64819次浏览 891人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187635次浏览 1123人参与
# 你怎么看待AI面试 #
180882次浏览 1318人参与
# 正在春招的你,也参与了去年秋招吗? #
364407次浏览 2642人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务