𝑰’𝒎 𝒉𝒉𝒈, 𝑰 𝒂𝒎 𝒂 𝒈𝒓𝒂𝒅𝒖𝒂𝒕𝒆 𝒔𝒕𝒖𝒅𝒆𝒏𝒕 𝒇𝒓𝒐𝒎 𝑵𝒂𝒏𝒋𝒊𝒏𝒈, 𝑪𝒉𝒊𝒏𝒂.
- 🏫 𝑺𝒉𝒄𝒐𝒐𝒍: 𝑯𝒐𝒉𝒂𝒊 𝑼𝒏𝒊𝒗𝒆𝒓𝒔𝒊𝒕𝒚
- 🌱 𝑳𝒆𝒂𝒓𝒏𝒊𝒏𝒈: 𝑰’𝒎 𝒄𝒖𝒓𝒓𝒆𝒏𝒕𝒍𝒚 𝒍𝒆𝒂𝒓𝒏𝒊𝒏𝒈 𝒅𝒆𝒔𝒊𝒈𝒏 𝒑𝒂𝒕𝒕𝒆𝒓𝒏, 𝑳𝒆𝒆𝒕𝒄𝒐𝒅𝒆, 𝒅𝒊𝒔𝒕𝒓𝒊𝒃𝒖𝒕𝒆𝒅 𝒔𝒚𝒔𝒕𝒆𝒎, 𝒎𝒊𝒅𝒅𝒍𝒆𝒘𝒂𝒓𝒆 𝒂𝒏𝒅 𝒔𝒐 𝒐𝒏.
- 💓 𝑯𝒐𝒘 𝒕𝒐 𝒓𝒆𝒂𝒄𝒉 𝒎𝒆:𝑽𝑿
- 📚 𝑴𝒚 𝒃𝒍𝒐𝒈: 𝒉𝒕𝒕𝒑𝒔://𝒉𝒉𝒈𝒚𝒚𝒅𝒔.𝒃𝒍𝒐𝒈.𝒄𝒔𝒅𝒏.𝒏𝒆𝒕/
- 💼 𝑷𝒓𝒐𝒇𝒆𝒔𝒔𝒊𝒐𝒏𝒂𝒍 𝒔𝒌𝒊𝒍𝒍𝒔:𝒎𝒚 𝒅𝒓𝒆𝒂𝒎
1-1: Overview
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
异位词 指由相同字母重排列形成的字符串(包括相同的字符串)。
示例 1:
输入: s = "cbaebabacd", p = "abc"
输出: [0,6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的异位词。
示例 2:
输入: s = "abab", p = "ab"
输出: [0,1,2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的异位词。
提示:
1 <= s.length, p.length <= 3 * 104
s 和 p 仅包含小写字母
通过次数174,661提交次数321,088
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-all-anagrams-in-a-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
1-1: Solution
After completing the analysis of the problem, I thought of the sliding window firstly. There are two windows. One is the fixed-size window. The other is the variable length window. They could also be applied to the solution to this question.
完成问题分析之后,我先想到了滑动窗口,有两种窗口一个是固定大小的,一个是可变长的,两种都可以应用到这道题目。
1-1-1: 定长滑动窗口
Variables
- counts[]: Record the frequency of each char within the string
p
ors[left, right]
; So, we can judge whethers[left, right]
is one arrangement of the string p; Why not use set? Set could only record the existence of the stringp
. However, the arrangement of stringp
is not only one. In other words, the chars inp
can be arranged in different orders. - left: the left boundary of the string to be checked
- right: the right boundary of the string to be checked
- counts 是用来记录p/s[left, right] 字符串中每个字符出现的频率,用来判断是否是一个排列。为什么不应set呢?set只能做到是否包含p.做不到是否包含p的一个排列
- left 左边界
- right 右边界
Code
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
int sLength = s.length();
int pLength = p.length();
if (pLength > sLength) {
return res;
}
// initial status
int left = 0;
int right = pLength - 1;
int[] sCounts = new int[26];
int[] pCounts = new int[26];
for (int i = 0; i < pLength; i++) {
sCounts[s.charAt(i) - 'a']++;
pCounts[p.charAt(i) - 'a']++;
}
if (Arrays.equals(pCounts, sCounts)) {
res.add(0);
}
// loop
while (right < sLength - 1) {
sCounts[s.charAt(left) - 'a']--;
left++;
right++;
sCounts[s.charAt(right) - 'a']++;
if (Arrays.equals(pCounts, sCounts)) {
res.add(left);
}
}
return res;
}
Within interations, change the sacle of the window, judge the string whether it is one arrangement of the pattern.
一次迭代中,改变滑动窗口的范围,判断是不是一个排列。
Complexity Analysis
1-1-2: 可变长滑动窗口
Code
public List<Integer> findAnagrams2(String s, String p) {
int[] cnt = new int[26];
List<Integer> res = new ArrayList<>();
for (char c : p.toCharArray()) {
cnt[c-'a']++;
}
int low = 0, high = 0;
while (high < s.length()) {
char charHigh = s.charAt(high);
if (cnt[charHigh - 'a'] > 0) {
cnt[charHigh - 'a']--;
high++;
if (high - low == p.length()) {
res.add(low);
}
} else {
char charLow = s.charAt(low);
cnt[charLow - 'a']++;
low++;
}
}
return res;
}
This solution is interesting. The high consume the counts[] and the low add the counts[]. The corner case is that the char charHigh
exists within s[left, righ]
and not exists within string p
; e.g. s[left, high]
= “aba”; p
= “ab”. cnt[high] = 0. From low to high(including high) counts[low++]++;
这个解决方法比较秒,high用来消费counts数组,low用来补充counts,临界条件就是遇见了不存在p中的字符,比如这里 s[left, high]
= “aba”; p
= “ab”. 此时cnt[high] = 0,从[low,high] 开始不足counts, 别忘记high 还是指向cnt[high] = 0的,补充完之后,当cnt[high] 由0变成1的时候,就会继续启动high开始消费,从而这个多余的字符就会被跳过去,继续开始新的一轮。关键是处理好那个corner case,临界情况,搞定滑动窗口是如何跳过那个多余的字符的。
Complexity Analysis
- time:O(n)
- space: O(1) fixed-size external space could be seen as 1. 固定大小的额外空间可以被当做常数