目录
一:题目链接
题目要求的是,假如字符串 s = "a b c a" ;字符串 p = "a b c" ,那么 p 就有 abc ,acb,bac,bca,cab,cba 这六种 “字母异位词” 的情况,要是 字符串 s 中的子串能找到符合其中的一个,就可以返回起始的下标。
二:题目思路
方法一:
我们可以通过暴力枚举的方式,先定义 left 和 right 指针在 字符串 s 的起始位置,定义存储符合条件的下标数组。
首先,对于字符串 s ,left 先固定,right 往后走 p.length() - 1 步判断当前left 和 right 区间的字母 与 字符串 p 是否符合 “字母异位词” 的情况,这里可以使用哈希表来实现判断的问题,要是符合情况,那么存储当前的 left 下标,left 和 right 走一步,保持当前 left 和 right 的区间(包括 left 和 right)大小为字符串 p 的长度 , 重复上述的流程,直到 left 走到字符串 s 的 s.length() - p.length() 位置结束。 返回得到的下标数组。
上述方法存在一些多余的操作,我们来优化一下。
方法二:
根据上述的暴力算法,我们知道,后续的 left 和 right 维护的区间大小就是固定的,判断符合条件是使用哈希表来判断当前 left 和 right 区间 的子串 和 字符串 p 是否符合 “字母异位词” ,使用哈希表一般都会比较麻烦且通过用时比较长,那么有什么高效的方法?
我们注意到,题目给出的提示说字符串 s 和 p 只包含小写字母,那么,我们就可以通过定义整形数组,[ 字符 - 'a' ] 表示对应的字母来当作数组的下标,元素大小表示该字母出现的次数。所以,我们可以通过判断当前有效元素个数是否等于字符串 p 的长度,具体怎么理解,如图:

首先,定义 left 和 right指针在字符串 s 起始位置,left 先固定,right 往后走,right 在此过程中不断把遇到的字母使用 [ 字母 - 'a' ] 来当作arr1整形数组的下标,再 arr1[字母 - 'a' ] ++,再与 已经完成计算字符串 p 字母个数的 arr2 数组 来 比较对应字母的个数,如果 arr1[字母 - 'a' ] <= arr2[字母 - 'a' ],证明当前字母是个有效的元素,此刻 m++ ,当 right 走到第二个 a 的时候,重复上面 arr1 添加字母个数的过程,即 arr1[字母 - 'a' ] ++,如果 arr1[字母 - 'a' ] > arr2[字母 - 'a' ] ,那么证明此时 right 遇到的字母 a 不是个有效的元素,m 不进行任何操作。直到 right 走到字母 b 的位置:

继续上述判断有效元素的操作,得到的字母 b 确实是一个有效的元素,此刻 m++,到这里,m = 2,此刻,判断 m 与 len 的值,可以看到,m 与 len 不相等,说明这不符合“字母异位词” 的情况,现在,保留 m 的值,right 继续 往后走:

此时,right 继续上述判断有效元素的操作,得到的字母 c 确实是一个有效的元素,此刻 m++,并且,left 和 right 的区间已经超过固定的大小了,此时,我们准备移动 left 了,移动 left 之前,先判断 left 位置的字母 arr1[字母 - 'a' ] <= arr2[字母 - 'a' ] 是否成立,如果不成立说明这是个无效的元素,所以 arr1[字母 - 'a' ]--, 最后再 left++:

left 操作完后,并且此刻 m 是等于 len 的,说明此刻 left 和 right 区间的字母和字符串 p 符合“字母异位词”的情况。
之后,right 继续往后走,不断重复上面的过程,到达字符串 s 末尾的位置结束。上述过程确实有些抽象,请务必结合下面代码来理解。
三:代码实现
//返回符合条件的下标
List<Integer> ret = new ArrayList<Integer>();
//将字符串转换成字符数组
char[] ss = s.toCharArray();
char[] pp = p.toCharArray();
//记录字符串 p 每个元素的个数
int[] arr2 = new int[26];
//记录字符串 p 的长度
int len = 0;
for(int i = 0;i < pp.length;i++) {
len++;
arr2[pp[i] - 'a']++;
}
//记录字符串 s 每个元素的个数
int[] arr1 = new int[26];
//记录字符串 s 有效字符个数
int m = 0;
int left = 0;
for(int right = 0;right < ss.length;right++) {
//进窗口
char come = ss[right];
if(++arr1[come - 'a'] <= arr2[come - 'a']) {
m++;
}
//判断出窗口条件
if(right - left + 1 > len) {
char out = ss[left];
if(arr1[out - 'a']-- <= arr2[out - 'a']) {
m--;
}
left++;
}
//更新结果
if(m == len) {
ret.add(left);
}
}
return ret;
1192

被折叠的 条评论
为什么被折叠?



