题目
给定两个字符串 s 和 p,找到 s 中所有 p 的 异位词 的子串,返回这些子串的起始索引。不考虑答案输出的顺序。
数据范围
1 <= s.length, p.length <= 3 * 104
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" 的异位词。
题解
博主题解(时间复杂度:O(m+(n−m)×Σ),其中 n 为字符串 s 的长度,m 为字符串 p 的长度,Σ 为所有可能的字符数(26个小写字母)。) 与官方题解思路一致,唯一区别就是写代码的习惯
class Solution {
//记录遍历过程中的hash数组
static int at[];
//记录p的哈希数组
static int ap[];
public List<Integer> findAnagrams(String s, String p) {
at=new int[27];
ap=new int[27];
List<Integer> res=new LinkedList<>();
if(s.length()<p.length())
return res;
int lenp=p.length();
int lens=s.length();
int ti=0;
char cp[]=p.toCharArray();
char cs[]=s.toCharArray();
for(int i=0;i<cp.length;i++)
ap[cp[i]-96]++;
while(ti<lenp){
at[cs[ti]-96]++;
ti++;
}
if(check()){
res.add(0);
}
int l=1;
int r=lenp;
while(r<lens){
at[cs[l-1]-96]--;
at[cs[r]-96]++;
if(check())
res.add(l);
l++;
r++;
}
return res;
}
public boolean check(){
boolean flag=true;
for(int i=1;i<=26;i++){
if(ap[i]!=at[i]){
flag=false;
break;
}
}
return flag;
}
}
官方题解二(官方题解一与我的方法近乎一致,就不重复了,题解二时间复杂度O(n+m+Σ))
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int sLen = s.length(), pLen = p.length();
if (sLen < pLen) {
return new ArrayList<Integer>();
}
List<Integer> ans = new ArrayList<Integer>();
int[] count = new int[26];
for (int i = 0; i < pLen; ++i) {
++count[s.charAt(i) - 'a'];
--count[p.charAt(i) - 'a'];
}
int differ = 0;
for (int j = 0; j < 26; ++j) {
if (count[j] != 0) {
++differ;
}
}
if (differ == 0) {
ans.add(0);
}
for (int i = 0; i < sLen - pLen; ++i) {
if (count[s.charAt(i) - 'a'] == 1) { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从不同变得相同
--differ;
} else if (count[s.charAt(i) - 'a'] == 0) { // 窗口中字母 s[i] 的数量与字符串 p 中的数量从相同变得不同
++differ;
}
--count[s.charAt(i) - 'a'];
if (count[s.charAt(i + pLen) - 'a'] == -1) { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从不同变得相同
--differ;
} else if (count[s.charAt(i + pLen) - 'a'] == 0) { // 窗口中字母 s[i+pLen] 的数量与字符串 p 中的数量从相同变得不同
++differ;
}
++count[s.charAt(i + pLen) - 'a'];
if (differ == 0) {
ans.add(i + 1);
}
}
return ans;
}
}
作者:力扣官方题解
链接:https://leetcode.cn/problems/find-all-anagrams-in-a-string/solutions/1123971/zhao-dao-zi-fu-chuan-zhong-suo-you-zi-mu-xzin/
来源:力扣(LeetCode)
思路
这道题很简单,个人认为不应该列入普通难度,因为这道题近乎没有思维难度,题目描述就是解题方法,我们们要做的也就是遍历数组,用滑动窗口维持p字符串的空间,然后将窗口内的子串与p对比就行了。
博主的思路就是简单的用hash存储窗口的子串与目标串,然后自写check()方法判断是否相同(也可以用官方题解的Arrays.equal判断,博主事先不知道这个方法)。 这个方法虽然简单,但够用。两种复杂度也达标。
官方的解法二十分巧妙,进一步优化了时间复杂度,他节约了每次去对比子串的时间,用一个变量differ从头到尾维护子串与目标串不同字符的个数,从而更加节约时间。