567. Permutation in String (M)
https://leetcode.com/problems/permutation-in-string/
Given two strings s1
and s2
, return true
if s2
contains a permutation of s1
, or false
otherwise.
In other words, return true
if one of s1
's permutations is the substring of s2
.
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
Constraints:
1 <= s1.length, s2.length <= 104
s1
and s2
consist of lowercase English letters.
Solution:
Version 1:
注意哦,输入的 s1
是可以包含重复字符的,所以这个题难度不小。
这种题目,是明显的滑动窗口算法,相当给你一个 S
和一个 T
,请问你 S
中是否存在一个子串,包含 T
中所有字符且不包含其他字符?
首先,先复制粘贴之前的算法框架代码,然后明确刚才提出的 4 个问题,即可写出这道题的答案:
// 判断 s 中是否存在 t 的排列
bool checkInclusion(string t, string s) {
unordered_map<char, int> need, window;
for (char c : t) need[c]++;
int left = 0, right = 0;
int valid = 0;
while (right < s.size()) {
char c = s[right];
right++;
// 进行窗口内数据的一系列更新
if (need.count(c)) {
window[c]++;
if (window[c] == need[c])
valid++;
}
// 判断左侧窗口是否要收缩
while (right - left >= t.size()) {
// 在这里判断是否找到了合法的子串
if (valid == need.size())
return true;
char d = s[left];
left++;
// 进行窗口内数据的一系列更新
if (need.count(d)) {
if (window[d] == need[d])
valid--;
window[d]--;
}
}
}
// 未找到符合条件的子串
return false;
}
JAVA Version:
class Solution {
public boolean checkInclusion(String s1, String s2) {
Map<Character,Integer> needs = new HashMap<Character,Integer>();
HashMap<Character, Integer> windows = new HashMap<Character, Integer>();
for(int i = 0;i< s1.length(); i++)
{
needs.put(s1.charAt(i), needs.getOrDefault(s1.charAt(i), 0)+1);
}
char[] elements= s2.toCharArray();
int windowSize = s1.length();
int start = 0;
int end = 0;
int validLetter = 0;
while(end< s2.length())
{
char currentEnd = s2.charAt(end);
end++;
if(needs.containsKey(currentEnd))
{
windows.put(currentEnd, windows.getOrDefault(currentEnd, 0)+1);
if(needs.get(currentEnd).equals(windows.get(currentEnd)))
{
validLetter++;
}
}
while(validLetter == needs.size())
{
if(end-start == s1.length()) return true;
char currentStart = s2.charAt(start);
start++;
if(needs.containsKey(currentStart))
{
if(needs.get(currentStart).equals(windows.get(currentStart)))
{
validLetter--;
}
windows.put(currentStart, windows.get(currentStart)-1);
}
}
}
return false;
}
}
对于这道题的解法代码,基本上和最小覆盖子串一模一样,只需要改变两个地方:
1、本题移动 left
缩小窗口的时机是窗口大小大于 t.size()
时,应为排列嘛,显然长度应该是一样的。
2、当发现 valid == need.size()
时,就说明窗口中就是一个合法的排列,所以立即返回 true
。
至于如何处理窗口的扩大和缩小,和最小覆盖子串完全相同。
Version 2:
https://www.jiuzhang.com/problem/permutation-in-string/
先扫一遍字符串s1,统计各个字母的个数,取s2前s1长度个字符,匹配个数是否相符,若不相符,去除最前面的字符,加入后一个字符,重新比对,直至个数匹配,或扫描完s2。
public class Solution {
public boolean checkInclusion(String s1, String s2) {
int len1 = s1.length(), len2 = s2.length();
if (len1 > len2) return false;
int[] count = new int[26];
for (int i = 0; i < len1; i++) {
count[s1.charAt(i) - 'a']++;
count[s2.charAt(i) - 'a']--;
}
if (allZero(count)) return true;
for (int i = len1; i < len2; i++) {
count[s2.charAt(i) - 'a']--;
count[s2.charAt(i - len1) - 'a']++;
if (allZero(count)) return true;
}
return false;
}
private boolean allZero(int[] count) {
for (int i = 0; i < 26; i++) {
if (count[i] != 0) return false;
}
return true;
}
}