题目
给定两个字符串 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 * 10^4s和p仅包含小写字母
我的思路
- 判断特殊情况,当s为空或者p为空时,返回空,s的长度小于p的长度时返回空
- 对p中的字母进行统计,因为p全为小写字母,用一个大小为26的数组进行统计
- 设置初始指针start和end为0和p.length-1,形成窗口,统计窗口中字母数量,和p的统计数组进行对比,出现一个不同则结束对比,完全相同则将此时的start索引加入结果列表,然后,更新s窗口统计数组,原来start所指字母的数量减1,start和end右移一个位置,新的end所指的字母数量加1
- 不断对比,至end到s的末端
- 返回结果列表
代码
java
package q438_find_anagrams;
import java.util.ArrayList;
import java.util.List;
public class FindAnagrams {
public List<Integer> findAnagrams(String s, String p) {
// 判断特殊情况,当s为空或者p为空时,返回空,s的长度小于p的长度时返回空
List<Integer> result=new ArrayList<>();
if(s.isEmpty() || p.isEmpty() || s.length()<p.length()){
return result;
}
// 对p中的字母进行统计,因为p全为小写字母,用一个大小为26的数组进行统计
char[] charp=p.toCharArray();
int[] countp=new int[26];
for (int i = 0; i < charp.length; i++) {
countp[charp[i]-'a']++;
}
// 设置初始指针start和end为0和p.length-1,形成窗口,
int start=0;
int end=charp.length-1;
char[] chars=s.toCharArray();
// 统计窗口中字母数量,和p的统计数组进行对比,
int k=0;
int[] counts=new int[26];
for (int j = start; j <=end; j++) {
counts[chars[j]-'a']++;
}
while (end<chars.length){
//出现一个不同则结束对比,
k=start;
while (k<=end){
if(countp[chars[k]-'a']!=counts[chars[k]-'a']){
break;
}
k++;
}
// 完全相同则将此时的start索引加入结果列表,
if(k==end+1){
result.add(start);
}
// 然后,更新s窗口统计数组,原来start所指字母的数量减1,start和end右移一个位置,新的end所指的字母数量加1
counts[chars[start]-'a']--;
start++;
end++;
if(end<chars.length){
counts[chars[end]-'a']++;
}
}
// 返回结果列表
return result;
}
public static void main(String[] args) {
String s="cbaebabacd";
String p="abc";
FindAnagrams fa=new FindAnagrams();
List<Integer> result=fa.findAnagrams(s,p);
System.out.println(result);
}
}
结果:时间击败18.27%,空间击败39.20%
根据官方题解优化后代码:
- 不用把字符串转换成字符数组,直接使用charAt方法获取对应位置的字符内容
- 将初始化p和s窗口中字符数量数组的过程由两个循环合并成一个循环
- 改变p和s窗口中字符数量统计数组的比较方式,直接使用Arrays的equals方法进行对比,能大幅缩短时间
优化后代码
package q438_find_anagrams;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class FindAnagrams {
public List<Integer> findAnagrams(String s, String p) {
// 判断特殊情况,当s为空或者p为空时,返回空,s的长度小于p的长度时返回空
List<Integer> result=new ArrayList<>();
if(s.isEmpty() || p.isEmpty() || s.length()<p.length()){
return result;
}
// 对p中的字母进行统计,因为p全为小写字母,用一个大小为26的数组进行统计
int[] countp=new int[26];
int[] counts=new int[26];
for (int i = 0; i < p.length(); i++) {
countp[p.charAt(i)-'a']++;
counts[s.charAt(i)-'a']++;
}
// 设置初始指针start和end为0和p.length-1,形成窗口,
int start=0;
int end=p.length()-1;
// 统计窗口中字母数量,和p的统计数组进行对比,
while (end<s.length()){
if(Arrays.equals(counts,countp)){
result.add(start);
}
// 然后,更新s窗口统计数组,原来start所指字母的数量减1,start和end右移一个位置,新的end所指的字母数量加1
counts[s.charAt(start)-'a']--;
start++;
end++;
if(end<s.length()){
counts[s.charAt(end)-'a']++;
}
}
// 返回结果列表
return result;
}
public static void main(String[] args) {
String s="cbaebabacd";
String p="abc";
FindAnagrams fa=new FindAnagrams();
List<Integer> result=fa.findAnagrams(s,p);
System.out.println(result);
}
}
Python
- 定义给定大小的整型数组:arr=[0]*size
- 数组对比,直接用 ==
- 字符转换成ascii码:ord()
class FindAnagrams():
def findAnagrams(self,s,p):
result=[]
# 判断特殊情况
if(len(s)<len(p)):
return result
# 统计初始窗口和p的各字母数量
pCount=[0]*26
sCount=[0]*26
for i in range(len(p)):
pCount[ord(p[i])-ord('a')]+=1
sCount[ord(s[i])-ord('a')]+=1
# 对比s窗口和p的统计结果
for i in range(len(s)-len(p)+1):
if pCount==sCount:
result.append(i)
sCount[ord(s[i])-ord('a')]-=1
if i+len(p)<len(s):
sCount[ord(s[i+len(p)])-ord('a')]+=1
return result
s="abab"
p="ab"
fa=FindAnagrams()
print(fa.findAnagrams(s,p))

8774

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



