• LeetCode 剑指Offer38 字符串的排列


    题目链接:LeetCode 剑指Offer38 字符串的排列

    题目大意:
    输入一个字符串,打印出该字符串中字符的所有排列。
    你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

    题解:

    回溯

    通过搜索和回溯枚举所有的排列情况,但会有重复的情况。
    只要在递归函数中设定一个规则,保证在填每一个空位的时候重复字符只会被填入一次。具体地,我们首先对原字符串排序,保证相同的字符都相邻,在递归函数中,我们限制每次填入的字符一定是这个字符所在重复字符集合中“从左往右第一个未被填入的字符”,即如下的判断条件:

    if (vis[j] || (j > 0 && !vis[j - 1] && s[j - 1] == s[j])) {
        continue;
    }
    

    这个限制条件保证了对于重复的字符,我们一定是从左往右依次填入的空位中的。

    class Solution {
    private:
        vector<string> ans;
        vector<int> vis;
    
    public:
        void backtrack(const string& s, int i, int n, string& now) {
            if (i == n) {
                ans.push_back(now);
                return;
            }
            for (int j = 0; j < n; j++) {
                if (vis[j] || (j > 0 && !vis[j - 1] && s[j - 1] == s[j])) {
                    continue;
                }
                vis[j] = true;
                now.push_back(s[j]);
                backtrack(s, i + 1, n, now);
                now.pop_back();
                vis[j] = false;
            }
        }
    
        vector<string> permutation(string s) {
            int n = s.size();
            vis.resize(n);
            sort(s.begin(), s.end());
            string now;
            backtrack(s, 0, n, now);
            return ans;
        }
    };
    

    尺取法

    按字典序从小到大寻找字符排列,注意到下一个排列总是比当前排列要大,除非该排列已经是最大的排列。我们希望找到一种方法,能够找到一个大于当前排列的新排列,且变大的幅度尽可能小。具体地:

    1. 我们需要将一个左边的“较小字符”与一个右边的“较大字符”交换,以能够让当前排列变大,从而得到下一个排列。
    2. 同时我们要让这个“较小字符”尽量靠右,而“较大字符”尽可能小。当交换完成后,“较大字符”右边的字符需要按照升序重新排列。这样可以在保证新排列大于原来排列的情况下,使变大的幅度尽可能小。

    具体地,我们这样描述该算法,对于长度为\(n\)的字符串\(s\)

    1. 首先从后向前查找第一个顺序对\((i,i+1)\),满足\(s[i]<s[i+1]\)。这样“较小字符”即为\(s[i]\)。此时\([i+1,n)\)必然是下降序列。
    2. 如果找到了顺序对,那么在区间\([i+1,n)\)中从后向前查找第一个元素\(j\)满足\(s[i]<s[j]\)。这样“较大字符”即为\(s[j]\)
    3. 交换\(s[i]\)\(s[j]\),此时可以证明区间\([i+1,n)\)必为降序。我们可以直接使用双指针反转区间\([i+1,n)\)使其变为升序,而无需对该区间进行排序。
    class Solution {
    public:
        bool nextPermutation(string& s) {
            int i = s.length() - 2;
            while (i >= 0 && s[i] >= s[i + 1]) {
                i--;
            }
            if (i < 0) {
                return false;
            }
            int j = s.length() - 1;
            while (s[i] >= s[j]) {
                j--;
            }
            swap(s[i], s[j]);
            reverse(s.begin() + i + 1, s.end());
            return true;
        }
    
        vector<string> permutation(string s) {
            vector<string> ans;
            sort(s.begin(), s.end());
            do {
                ans.push_back(s);
            } while (nextPermutation(s));
            return ans;
        }
    };
    
  • 相关阅读:
    Redis 系列之CentOS下Redis的安装
    Redis 系列之CentOS下Redis的安装
    Redis 系列之CentOS下Redis的安装
    Redis 系列之CentOS下Redis的安装
    55种网页常用小技巧
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/IzumiSagiri/p/15874607.html
Copyright © 2020-2023  润新知