【重点】【回溯】【全排列】剑指offer——面试题28:字符串的排列

力扣,https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/description/
全排列问题

Python,下一个排列

class Solution:
    def goodsOrder(self, goods: str) -> List[str]:
        res, visited = list(), set()
        self.func(0, list(goods), res, visited)

        return res
    
    def func(self, start, array, res, visited):
        if start == len(array) - 1:
            tmp_str = ''.join(array)
            if tmp_str not in visited:
                res.append(tmp_str)
                visited.add(tmp_str)
        for i in range(start, len(array)):
            array[start], array[i] = array[i], array[start]
            self.func(start+1, array, res, visited)
            array[start], array[i] = array[i], array[start]

方法1:DFS+剪枝

// 版本1:dfs + 剪枝,前提:我们首先对原商品串排序,保证相同的商品都相邻!!!
class Solution {
    public String[] goodsOrder(String goods) {
        List<String> res = new ArrayList<>(); // 增加元素时,List比Set更快!!!
        int[] used = new int[goods.length()];
        String out = "";
        char[] arr = goods.toCharArray(); // 学习String转char[]的方法
        Arrays.sort(arr);
        dfs(arr, used, res, out);
        return res.toArray(new String[res.size()]); // List转数组[]方法
    }

    public void dfs(char[] arr, int[] used, List<String> res, String out) {
        if (out.length() == arr.length) {
            res.add(out.toString());
            return;
        }
        for (int i = 0; i < used.length; ++i) {
            if (used[i] == 1 
                    || (i > 0 && used[i - 1] == 0 && arr[i - 1] == arr[i])) { // 这个限制条件保证了对于重复的商品,我们一定是从左往右依次填入的空位中的。
                continue;
            }
            used[i] = 1;
            dfs(arr, used, res, out + arr[i]);
            used[i] = 0; // 回溯, 修改状态, out自动减去了 s.charAt(i)
        }
    }
}

// 版本2:无剪枝,使用StringBuffer
class Solution {
    public String[] goodsOrder(String goods) {
        Set<String> res = new HashSet<>();
        int left = goods.length();
        boolean[] used = new boolean[left];
        Arrays.fill(used, false);
        StringBuffer sBuffer = new StringBuffer();
        dfs(goods, used, res, sBuffer, left);
        return res.toArray(new String[res.size()]);
    }

    public void dfs(String goods, boolean[] used, Set<String> res, StringBuffer tmpStr, int left) {
        if (left == 0) {
            res.add(tmpStr.toString()); // StringBuffer的toString()方法
            return;
        }
        for (int i = 0; i < used.length; ++i) {
            if (used[i]) {
                continue;
            }
            used[i] = true;
            tmpStr.append(goods.charAt(i)); // StringBuffer使用append()增加char
            dfs(goods, used, res, tmpStr, left - 1);
            used[i] = false;
            tmpStr.deleteCharAt(tmpStr.length() - 1); // // StringBuffer删除指定位置的char
        }
    }
}

// 版本3:无剪枝,简练版回溯!
class Solution {
    public String[] goodsOrder(String goods) {
        Set<String> res = new HashSet<>();
        int[] used = new int[goods.length()];
        String out = "";
        dfs(goods, used, res, out);
        return res.toArray(new String[res.size()]);
    }

    public void dfs(String goods, int[] used, Set<String> res, String out) {
        if (out.length() == goods.length()) {
            res.add(out.toString()); // 注意:String也有方法:toString()
            return;
        }
        for (int i = 0; i < used.length; ++i) {
            if (used[i] == 1) {
                continue;
            }
            used[i] = 1;
            dfs(goods, used, res, out + goods.charAt(i));
            used[i] = 0; // 回溯, 修改状态, out自动减去了 s.charAt(i)
        }
    }
}

方法2:下一个排列

思路:https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/solutions/178988/mian-shi-ti-38-zi-fu-chuan-de-pai-lie-hui-su-fa-by/
从全排列公式来理解:n! = n * (n-1) * (n-2)… 2 * 1。
在这里插入图片描述
在这里插入图片描述

class Solution {
    public String[] goodsOrder(String goods) {
        List<String> res = new ArrayList<>();
        char[] array = goods.toCharArray();
        dfs(0, array, res);
        return res.toArray(new String[res.size()]);
    }

    public void dfs(int startInx, char[] array, List<String> res) {
        if (startInx == array.length - 1) {             // 最后一位已经定了
            res.add(String.valueOf(array));             // String.valueOf()把char[]转为String
            return;
        }
        Set<Character> uesdCharSet = new HashSet<>();   // 记录用过的字符, 重复的字符不必再交换
        for (int i = startInx; i < array.length; ++i) { // 包含startInx原因是考虑索引i的char不变的情况
            if (uesdCharSet.contains(array[i])) {
                continue; // 剪枝
            }
            uesdCharSet.add(array[i]);
            swap(i, startInx, array);      // 交换, 将arr[i]固定在第startInx位
            dfs(startInx + 1, array, res); // 下一轮递归, 开启固定第startInx+1位字符
            swap(i, startInx, array);      // 复原
        }
    }

    public void swap(int i, int j, char[] array) {
        char tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }
}

Solution1:
2018年9月2日重做
典型的DFS套路,LeetCode 46 && 47
【46】https://blog.youkuaiyun.com/Allenlzcoder/article/details/80271771
【47】https://blog.youkuaiyun.com/Allenlzcoder/article/details/80275707,
重写一下:

class Solution {
public:
    vector<string> Permutation(string str) {
        if (!str.size()) return {};
        if (str.size() == 1) return vector<string> {str};
        set<string> tmp_res;
        //未访问过 0; 访问过 1
        vector<int> visited(str.size(), 0);
        int level = 0;
        string tmp_str;
        my_dfs(str, tmp_res, tmp_str, visited, level);
        return vector<string> (tmp_res.begin(), tmp_res.end());
    }
    
    void my_dfs(string &str, set<string> &tmp_res, 
                string &tmp_str, vector<int> &visited, int level) {
        if (level == str.size()) {
            tmp_res.insert(tmp_str);
            return;
        } else {
            for (int i = 0; i < str.size(); i++) {
                if (visited[i]) continue;
                else {
                    tmp_str += str[i];
                    visited[i] = 1;
                    my_dfs(str, tmp_res, tmp_str, visited, level + 1);
                    tmp_str.pop_back();
                    visited[i] = 0;
                }
            }
        }
    }
};

Soltion2:
第一次做题时的答案
发现了一个代码风格很清新的博客,网址是:http://www.cnblogs.com/qqky/
此题答案就是根据此博客整理而来~

class Solution {
public:
    vector<string> Permutation(string str) {
        vector<string> res;
        if(str.empty())
            return res;
        FindAll(str, 0, res);
        sort(res.begin(),res.end());
        return res;
    }
    
    void FindAll(string s, int begin, vector<string> &result){ //遍历第begin位的所有可能
        //一次遍历的结束条件
        if(begin == s.size()-1)
            result.push_back(s);
        for(int i = begin; i<s.size(); i++){
            if(i!=begin && s[i] == s[begin])
                continue;//有与begin位重复的字符串不进行交换,跳过
            
            swap(s[i],s[begin]);
            
            //当i==begin时,也要遍历其后面的所有字符
            //当i!=begin时,先交换,使第begin位取到不同的可能字符,再遍历后面的字符
            FindAll(s,begin+1,result);
            swap(s[i],s[begin]);//为了防止重复的情况,还需要将begin处的元素重新换回来
        }
        return;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值