leetcode438_找到字符串中所有字母异位词
438. 找到字符串中所有字母异位词
方法一:简单滑动窗口
满足异位词条件:
(1)s中子串s' 与 目标字符串p的长度相等
(2)s'与p中字母相同(对排列方式没有要求)
算法思路:在字符串s中构造一个长度与字符串p的长度相同的滑动窗口截取子串s‘,并在窗口中维护每种字母的数量。当s'的每种字母数量与目标串p的每种字母数量相同,则说明当前窗口为字符串p的异位词。
技巧:可以使用数组记录字符串每种字母的个数,最后比较s'与p的记录数组即可。
public List<Integer> findAnagrams(String s, String p) {
int[] pCount = new int[26];
List<Integer> result = new ArrayList<>();
int sLength = s.length();
int pLength = p.length();
if (sLength < pLength) {
return result;
}
for (int i = 0; i < pLength; i++) {
++pCount[p.charAt(i) - 'a'];
}
int lo = 0;
int hi = pLength;
// [lo,hi)
while (hi <= sLength) {
int[] sCount = new int[26];
int x = lo;
for (int i = lo; i < hi; i++) {
++sCount[s.charAt(i) - 'a'];
}
if (Arrays.equals(sCount, pCount)) {
result.add(x);
}
// 滑动窗口
hi++;
lo++;
}
return result;
}
方法二 :改进滑动窗口
方法一中每次都需要对子串s' 进行重新统计,然后再一一比较。可以每次仅仅考虑lo收缩减少的那个字母以及hi扩张增加的字母。使用diff表示当前存在的数量不同的字母种数。
public List<Integer> findAnagrams(String s, String p) {
int[] count = new int[26];
List<Integer> result = new ArrayList<>();
int sLength = s.length();
int pLength = p.length();
if (sLength < pLength) {
return result;
}
for (int i = 0; i < pLength; i++) {
--count[p.charAt(i) - 'a'];
++count[s.charAt(i) - 'a'];
}
//指示子串s'与字符串中不同字母的种数
int diff = 0;
for (int i = 0; i < 26; i++) {
if (count[i] != 0) {
diff++;
}
}
if (diff == 0) {
result.add(0);
}
int lo = 0;
for (; lo < sLength - pLength; ) {
// 出lo
char ch = s.charAt(lo);
if (count[ch - 'a'] == 0) {
diff++;
} else if (count[ch - 'a'] == 1) {
diff--;
}
--count[ch - 'a'];
char ch1 = s.charAt(lo + pLength);
if (count[ch1 - 'a'] == -1) {
diff--;
} else if (count[ch1 - 'a'] == 0) {
diff++;
}
++count[ch1 - 'a'];
lo++;
if (diff == 0) {
result.add(lo);
}
}
// [lo,hi)
return result;
}
变式
若存在两个字符串t与p,若要求字符串t的长度为|p|+ 4的子串中包含p的全部字母即p为t的异位词,那么判断p是否为t的异位词。(保证|s| > |p|)
对方法而进行修改:
(1)tLenght = min(t.length(), pLength + 4);
(2)第一次对diff取值时,只要count[*] >= 0 即可(表示子串t'中包含了模版p的全部内容)
(3)窗口移除lo时,只需要关注count[*]是否从0 变为负数,这意味增加了一个不满足条件的字母
(4)窗口新增hi时,只需要关注count[*]是否从-1 变为0,这意味着减少了一个不满足条件的字母