【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:
-
The input strings only contain lower case letters.
-
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;
}
};