【LeetCode & 剑指offer刷题】字符串题9:38 字符串的排列(全排列问题)

【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

38 字符串的排列(全排列问题)

题目描述

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

输入描述:

输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
 
/*
问题:全排列(含重复元素,且要求按字典序输出)
方法一:交换法,递归
用哈希表记录,以解决存在重复元素的全排列问题
参考问题总结“排列与组合”
*/
class Solution
{
public:
    vector<string> Permutation(string str)
    {
        vector<string> result;
        if(str.empty()) return result;
        per(str, 0, result);
        sort(result.begin(), result.end()); //排序,以便之后产生字典序的全排列(这里是产生序列之后再排序,通过改进算法可以实现按字典序push)
        return result;
    }
private:
    void per(string& str, int begin, vector<string>& result)
    {
        if(begin >= str.size()-1)
        {
            result.push_back(str);
            return;
        }
        else
        {
            unordered_set<char> record; //记录出现过的字符
            for(int i = begin; i<str.size(); i++) //产生排列的多个分支
            {
                if(record.find(str[i]) == record.end()) //只和没交换过的交换
                {
                    record.insert(str[i]);
                    swap(str[begin], str[i]); //与后面元素交换,以产生不同字符开头的排列
                    per(str, begin+1, result); //递归产生分支的深度
                    swap(str[begin], str[i]); //归位,以便下次交换                   
                }
            }
        }
       
    }
};
 
/*
方法二:dfs(掌握)
map记录,以解决存在重复元素的全排列问题
*/
class Solution
{
public:
    vector<string> Permutation(string str)
    {
      
        vector<string> result;
        if(str.empty()) return result;
       
        string path;
        map<char,int> counter; //map时,key值存储为有序的,最后输出的排列也是有序的      
       // sort(str.begin(), str.end()); //上面用了map,故这里无需先sort      
        for(char a:str) counter[a]++; //统计str中各数出现的次数,没有key的时候会自动创建      
        dfs(str, result, path, counter);//递归
        return result;
    }
private:
    void dfs(string& str, vector<string>& result, string& path, map<char, int>& counter)
    {
        if(path.size() == str.size()) //到达树的末尾,将单路径数组push到结果向量中
        {
            result.push_back(path);
            return;
        }
      
        for(auto& p:counter) //for循环带来的是树宽度方向的延伸,即产生同一层的多个分支
        {
            if(p.second>0) //如果该元素没有被取完(某个元素可能会出现多次)
            {
                path.push_back(p.first);
                p.second--; //已经取了这个元素,统计数减一
                dfs(str, result, path, counter); //继续往深度方向延伸
                path.pop_back();  //回溯,给其他分支腾空间!!
                p.second++;
            }
        }
    }
};
 
 
567. Permutation in String
Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. In other words, one of the first string's permutations is the substring of the second string.
Example 1:
Input:s1 = "ab" s2 = "eidbaooo"
Output:True
Explanation: s2 contains one permutation of s1 ("ba").
Example 2:
Input:s1= "ab" s2 = "eidboaoo"
Output: False
Note:
  1. The input strings only contain lower case letters.
  2. The length of both given strings is in range [1, 10,000].

 
/*
问题:判断s1的全排列中是否有排列是s2的子串
统计表+滑动窗口
统计s1各字符出现的次数,并用s1长度的滑动窗框住s2,统计各滑动窗各字符出现的次数,如果与s1中相等,则返回true
*/
class Solution
{
public:
    bool checkInclusion(string s1, string s2)
    {
        if(s1.empty() || s2.empty()) return false;
       
        int n1 = s1.size(), n2 = s2.size();
        vector<int> m1(128), m2(128);//统计表(也可用hash表)
       
        for (int i = 0; i < n1; ++i) //对s1,s2中前n1个字符统计
        {
            m1[s1[i]]++; m2[s2[i]]++;
        }
       
        if (m1 == m2) return true; //若各字符出现的次数相等,说明可以由s1排列得到当前s2中的子串
       
        for (int i = n1; i < n2; ++i) //滑动窗口扫描s2
        {
            m2[s2[i]]++;
            m2[s2[i - n1]]--;
            if (m1 == m2) return true;
        }
        return false;
    }
};
 
 

 

posted @ 2019-01-05 15:56  wikiwen  阅读(663)  评论(0编辑  收藏  举报