Solution
思路1:
滑动窗口。维护窗口内字符的数量,判断与\(p\)字符串数量是否相等。复杂度\(O((sLen-pLen)*26)\)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<>();
}
List<Integer> ans = new ArrayList<>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < pLen; i++) {
pCount[p.charAt(i) - 'a']++;
sCount[s.charAt(i) - 'a']++;
}
if (Arrays.equals(sCount, pCount)) {
ans.add(0);
}
for (int i = 0; i < sLen - pLen; i++) {
sCount[s.charAt(i) - 'a']--;
sCount[s.charAt(i + pLen) - 'a']++;
if (Arrays.equals(sCount, pCount)) {
ans.add(i + 1);
}
}
return ans;
}
}
思路2:
滑动窗口优化。不维护窗口与\(p\)字符串字符的数量,维护他们的差值\(diff\),\(diff\)为\(0\),即为异位词。判断左边界和右边界对\(diff\)的贡献。
左边界考虑\(1\)和\(0\):
- 若为\(1\),滑动后\(diff+1\)
- 若为0,滑动后\(diff-1\)
右边界考虑\(-1\)和\(0\):
- 若为\(-1\),滑动后\(diff+1\)
- 若为0,滑动后\(diff-1\)
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<>();
}
List<Integer> ans = new ArrayList<>();
int[] count = new int[26];
for (int i = 0; i < pLen; i++) {
count[p.charAt(i) - 'a']--;
count[s.charAt(i) - 'a']++;
}
int diff = 0;
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
diff++;
}
}
if (diff == 0)
ans.add(0);
for (int i = 0; i < sLen - pLen; i++) {
if (count[s.charAt(i) - 'a'] == 1) {
diff--;
} else if (count[s.charAt(i) - 'a'] == 0) {
diff++;
}
count[s.charAt(i) - 'a']--;
if (count[s.charAt(i + pLen) - 'a'] == -1) {
diff--;
} else if (count[s.charAt(i + pLen) - 'a'] == 0) {
diff++;
}
count[s.charAt(i + pLen) - 'a']++;
if (diff == 0) {
ans.add(i + 1);
}
}
return ans;
}
}
思路3:
滑动窗口+双指针。如果\(s\)的某个字符多了,肯定就不符合,所以只能通过不停弹出\(l\)来符合;只有字符不超过\(p\)的字符数量且长度与\(p\)一致,才为答案。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<>();
}
List<Integer> ans = new ArrayList<>();
int[] sCount = new int[26];
int[] pCount = new int[26];
for (int i = 0; i < pLen; i++) {
pCount[p.charAt(i) - 'a']++;
}
int l = 0;
for (int r = 0; r < sLen; r++) {
int right = s.charAt(r) - 'a';
sCount[right]++;
while (sCount[right] > pCount[right]) {
int left = s.charAt(l) - 'a';
sCount[left]--;
l++;
}
if (r - l + 1 == pLen) {
ans.add(l);
}
}
return ans;
}
}
思路4:
将\(p\)看做资源去消耗,能不能把当前\(s\)吃掉,吃掉后\(r++\),有可能长度与\(pLen\)一致,如果吃不掉当前\(s\),那就收回之前的\(l++\),如果出现未出现的字符,加上后也会消耗掉。
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
List<Integer> ans = new ArrayList<>();
int[] count = new int[26];
for (int i = 0; i < pLen; i++) {
count[p.charAt(i) - 'a']++;
}
int l = 0, r = 0;
while (r < sLen) {
if (count[s.charAt(r) - 'a'] > 0) {
count[s.charAt(r++) - 'a']--;
if (r - l == pLen)
ans.add(l);
} else {
count[s.charAt(l++) - 'a']++;
}
}
return ans;
}
}