Leetcode每日一题:438. 找到字符串中所有字母异位词

bym思路1 、排序法:将两个字符串转换为字符数组,排序后比较是否相等。
bym思路2 、哈希表法:使用一个长度为26的数组(假设只包含小写字母)统计每个字母出现的次数,比较两个字符串的统计结果。这里我把每一个子串都遍历出来了,不知道滑动窗口能用在哪里,看了灵神的灵魂拷问"暴力枚举所有子串?时间复杂度是 O(nk),太慢了。"+图解[1](#1),结合绘图和"方法一:定长滑窗"的代码[2](#2),终于理解滑动窗口妙在哪里了。bym妙就妙在切换下一个子串时,暴力遍历仍需要枚举整个字串,而滑动窗口只需移除左指针的元素,添加一个右指针的元素即可 。

灵神代码节选:
java
for (int right = 0; right < s.length(); right++) {
cntS[s.charAt(right) - 'a']++; // **右端点字母进入窗口**
int left = right - p.length() + 1;
if (left < 0) { // 窗口长度不足 p.length()
continue;
}
if (Arrays.equals(cntS, cntP)) { // s' 和 p 的每种字母的出现次数都相同
ans.add(left); // s' 左端点下标加入答案
}
cntS[s.charAt(left) - 'a']--; // **左端点字母离开窗口**
}
by评论区:思考心路历程可以借鉴
做了四十多道题了,也悟出来了一些东西。
其实很多看起来高大上的解法,比如说这里的双指针,都是从最原始最暴力的解法出发不断进行优化。在这里,不难写出暴力解:遍历所有子串,一一与目标串进行比对。
然后我们有两个优化方向:
第一,剪枝。其实通过排除一些条件,就可以无需遍历所有的子串。比方说,if(s.size() - lo < p.size()) break; 当然,更主要的,如果当前遍历到的字符根本不属于目标串,那包含这个字符的子串都无需再看。
其实滑动窗口和双指针就是基于上述剪枝的思路。设置两个指针lo和hi,如果hi指针推入窗口的元素根本不属于目标串,或者数量已经超过目标串中对应字母的数量(两者本质上其实是一样的),那就让lo指针右移,挤出元素。
第二,找到一个高效的比较当前子串与目标串的算法,看是否是异位词。我是这样做的:通过一个大小为26的int数组来记录目标串中每一个字母出现的次数,用这个数组来动态表示当前的目标串。还需要一个int整数来记录字母的种类数。当int整数值为零,也就说明目标串为空。换言之,当前子串是目标串的异位词。
以上算法,时间复杂度O(n), 0ms通过,空间复杂度O(1)