Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。
题目:给定两个字符串s和p,找到s中p的异位词子串,返回子串的起始索引。
解题思路
本题思路很好想,首先看两个字符串的长度,如果s长度小于p,那么直接可以返回空列表。如果s长度大于p,只需要将s从首位开始遍历,每次遍历判断从遍历起点开始到加上p长度的这一串字符串是否是字母异位词即可,如何判断字母异位词是本题的关键。
思路一
如何判断两个字符串是异位词,首先想到的是两个由相同字母组成的字符串有什么特征呢?
- 字符串转数组后排序必定相同
根据这个特征,我们可以首先对p字符串进行排序,之后每次截取s中和p等长的字符串,对该字符串也进行排序,然后判断两个字符数组是否相等即可,可得代码如下:
public List<Integer> findAnagrams3(String s, String p) {
int sLen = s.length();
int pLen = p.length();
if (sLen < pLen) return new ArrayList<>();
ArrayList<Integer> res = new ArrayList<>();
char[] pChar = p.toCharArray();
Arrays.sort(pChar);
for(int i=0;i<=sLen-pLen;i++){
char[] cChar = s.substring(i, i+pLen).toCharArray();
Arrays.sort(cChar);
if(Arrays.equals(cChar, pChar)){
res.add(i);
}
}
return res;
}
时间复杂度为,有点高,最终耗时1800ms+。
思路二
上面思路的方法慢主要是因为统计两个待比较的数组时比较耗时,那么我们可否换一种思路来获得两个待比较的数组。
因为字符串中字符的范围是确定的(a-z),因此我们可以初始化两个长度为26的数组,数组中统计每个元素出现的次数,之后根据比较这两个数组的亦同即可得到最终答案。
此处有一个要点在于我们要首先统计0索引处是否是字母异位词,之后统计1时就可以再次从0开始,每次更新起始索引和起始索引+pLen处的元素,可得代码如下:
public List<Integer> findAnagrams(String s, String p){
int sLen = s.length();
int pLen = p.length();
if(sLen<pLen) return new ArrayList<>();
ArrayList<Integer> res = new ArrayList<>();
int[] pCount = new int[26];
int[] sCount = new int[26];
for(int i=0;i<pLen;i++){
pCount[p.charAt(i)-'a']++;
sCount[s.charAt(i)-'a']++;
}
if(Arrays.equals(pCount,sCount)) res.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)){
res.add(i+1);
}
}
return res;
}
时间复杂度明显下降,此时为。
优化
实际上上述代码还可以优化,我们可以只使用一个数组来统计s和p的字符,每次维护这一个数组即可。不过这个方法比较巧妙,需要自己模拟一遍,代码如下:
public List<Integer> findAnagrams2(String s, String p) {
int sLen = s.length();
int pLen = p.length();
if (sLen < pLen) return new ArrayList<>();
ArrayList<Integer> res = new ArrayList<>();
int[] count = new int[26];
int differ = 0;
for(int i=0;i<pLen;i++){
count[s.charAt(i)-'a']++;
count[p.charAt(i)-'a']--;
}
for(int i=0;i<count.length;i++){
if(count[i]!=0) differ++;
}
if(differ==0) res.add(0);
for(int i=0;i<sLen-pLen;i++){
if(count[s.charAt(i)-'a']==1){
differ--;
}else if(count[s.charAt(i)-'a']==0){
differ++;
}
count[s.charAt(i)-'a']--;
if(count[s.charAt(i+pLen)-'a']==0){
differ++;
}else if(count[s.charAt(i+pLen)-'a']==-1){
differ--;
}
count[s.charAt(i+pLen)-'a']++;
if(differ==0) res.add(i+1);
}
return res;
}