leetcode_438 找到字符串中的所有异位词
1. 题意
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,
返回这些子串的起始索引。不考虑答案输出的顺序。
异位词的意思是两个字符串的所有字符种类相同,各个种类对应的
数量相同,顺序可以不相同。
比如aabbcaabbcaabbc与ababcababcababc就是异位词。
2. 题解
这是一个典型的滑动窗口的问题,我们需要计算字符串sss中每个
p.size()p.size()p.size()大小的窗口内的字符串与sss是否是异位词。
2.1 定长滑动窗口+两个哈希表
用一个哈希表存ppp中各个字符的次数,用另一个哈希表存sss的当前滑窗
中字符出现的次数。如果两个哈希表各个次数相等,说明当前窗口内的
字符串是ppp的一个异位词。
窗口左端出窗口,窗口右端进入新的元素。重复处理直到遍历完sss。
class Solution {
public:vector<int> findAnagrams(string s, string p) {int n = s.size();int plen = p.size();unordered_map<int,int> cur;unordered_map<int,int> tar;vector<int> ans;for (auto c: p)tar[c]++;for (int i = 0; i < n; ++i) {if ( i >= plen )cur[s[i - plen]]--;cur[s[i]]++;if (i >= plen - 1) {bool eq = true;for (auto &[k,v]: tar) {if ( cur[k] != v ) {eq = false;break;}}if (eq) {ans.push_back( i + 1 - plen );}}}return ans;}
};
可以用std::array<int,26>std::array<int,26>std::array<int,26>来代替,而且可以直接判断相等。
class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> ans;array<int,26> cnt1;array<int,26> cnt2;for (auto c: p) {cnt1[c - 'a']++;} int n = s.size();for (int i = 0; i < n; ++i) {cnt2[ s[i] - 'a']++;if ( i >= p.size())cnt2[ s[i - p.size()] - 'a']--;if ( cnt1 == cnt2)ans.push_back( i + 1 - p.size() );}return ans;}
};
2.2 定长滑动窗口+一个哈希表
我们用哈希表来记录当前窗口内字符串字符和ppp内字符串的差值。
用diffdiffdiff表示有几种字符还不相同。
对于左端出窗口的字符串来说,
cnt[s[l]]−−cnt[s[l]]--cnt[s[l]]−−
如果cnt[s[l]]==1cnt[s[l]] == 1cnt[s[l]]==1,
说明刚好多了一个这种字符,它出去了说明有一种字符就相同了,
因此diff++diff++diff++。
如果cnt[s[l]]==0cnt[s[l]]==0cnt[s[l]]==0,
说明这种字符本来是数量一致的,它出窗口了有一种字符就对不上了,
因此diff−−diff--diff−−。
对于窗口右端的字符来说,
cnt[s[r]]++cnt[s[r]]++cnt[s[r]]++
如果cnt[s[r]]==−1cnt[s[r]] == -1cnt[s[r]]==−1,
说明新的元素刚好填上一种字符的缺口,因此diff−−diff--diff−−;
如果cnt[s[r]]==0cnt[s[r]]==0cnt[s[r]]==0,
说明新元素就多余了,因此diff++diff++diff++。
class Solution {
public:vector<int> findAnagrams(string s, string p) {if ( s.size() < p.size()) {return vector<int>();}int n = s.size();int plen = p.size();vector<int> ans;vector<int> cnt(26, 0);for (int i = 0; i < plen; ++i) {++cnt[s[i] - 'a'];--cnt[p[i] - 'a'];}int diff = 0;for (int i = 0; i < 26; ++i) {if ( 0 != cnt[i])++diff;}if ( 0 == diff )ans.push_back( 0 );for (int i = plen; i < n; ++i) {int l = s[i - plen] - 'a';if ( cnt[ l ] == 1) {--diff;}else if ( cnt[ l ] == 0) {++diff;}--cnt[ l ];int r = s[i] - 'a';if ( cnt[r] == -1 ) {--diff;}else if ( cnt[r] == 0 ) {++diff;}++cnt[ r ];if ( diff == 0) {ans.push_back( i + 1 - plen);}}return ans;}
};
2.3 不定滑窗
我们枚举字符串s′s's′的右端点,
使得它满足s′s's′中所有的字符个数是小于等于sss中的所有的字符个数的。
当一个新元素进入滑窗后使得这个性质改变了,那么就不断窗口左端,
使得窗口满足这个性质。满足这个性质后我们再判断窗口长度是否等于
p.size()p.size()p.size(),长度相等也就说明了两个字符串是异位词,
将之放入答案序列。
class Solution {
public:vector<int> findAnagrams(string s, string p) {vector<int> ans;array<int,26> cnt;for (auto c: p) {cnt[c - 'a']++;} int n = s.size();int l = 0;for (int r = 0; r < n; ++r) {int v = s[r] - 'a';cnt[ v ]--;while ( cnt[v] < 0) {++cnt[ s[l] - 'a'];++l;}if ( r - l + 1 == p.size()) {ans.push_back( l );}}return ans;}
};
3. 参考
0x3f
leetcode