文章目录
1. 题目描述
给定两个字符串 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^4
s
和p
仅包含小写字母
2. 理解题目
这道题要求我们找出字符串 s
中所有与字符串 p
构成字母异位词的子串的起始索引。我们需要理解以下几点:
- 字母异位词:由相同字母以不同顺序组成的字符串,比如"abc"和"bca"互为字母异位词。
- 子串:必须是连续的字符序列。
- 返回值:需要返回所有匹配子串的起始索引的列表。
关键点:
- 我们只需要比较子串和目标字符串
p
包含的字符及其出现次数是否相同。 - 子串的长度必须与
p
相同。 - 我们不关心字符在子串中的顺序,只关心每种字符出现的次数。
3. 解法一:暴力法 + 排序
3.1 思路
最直观的方法是遍历字符串 s
中所有长度为 p.length()
的子串,判断它们是否是 p
的字母异位词。
具体算法:
- 对字符串
p
进行排序,得到有序字符串sortedP
。 - 遍历字符串
s
中所有长度为p.length()
的子串:- 对每个子串进行排序
- 比较排序后的子串是否等于
sortedP
- 如果相等,将子串起始索引加入结果列表
3.2 Java代码实现
public class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>();
if (s == null || p == null || s.length() < p.length()) {
return result;
}
// 对字符串p进行排序
char[] pArray = p.toCharArray();
Arrays.sort(pArray);
String sortedP = new String(pArray);
// 遍历s中所有长度为p.length()的子串
int pLength = p.length();
for (int i = 0; i <= s.length() - pLength; i++) {
// 获取当前子串
String substring = s.substring(i, i + pLength);
// 对子串进行排序
char[] subArray = substring.toCharArray();
Arrays.sort(subArray);
String sortedSub = new String(subArray);
// 比较排序后的字符串
if (sortedSub.equals(sortedP)) {
result.add(i);
}
}
return result;
}
}
3.3 代码详解
- 首先处理边界情况:如果输入字符串为空或者
s
的长度小于p
的长度,直接返回空列表。 - 对字符串
p
进行排序,得到有序字符串sortedP
。 - 遍历字符串
s
中所有可能的起始位置:- 对于每个位置
i
,提取长度为p.length()
的子串。 - 对子串进行排序,得到有序子串
sortedSub
。 - 比较
sortedSub
是否等于sortedP
,如果相等,将起始索引i
加入结果列表。
- 对于每个位置
3.4 复杂度分析
- 时间复杂度:O((n-m+1) * m * log(m)),其中 n 是字符串
s
的长度,m 是字符串p
的长度- 我们需要检查 (n-m+1) 个子串
- 对每个子串进行排序需要 O(m * log(m)) 的时间
- 空间复杂度:O(m),需要存储排序后的字符串
3.5 问题与改进
暴力法虽然直观,但效率较低,特别是当 s
和 p
较长时。每次排序和创建新字符串的操作都会带来额外的开销。我们可以使用字符计数的方法来优化。
4. 解法二:字符计数法
4.1 思路
既然字母异位词是由相同字母以不同顺序组成的字符串,那么它们包含的每种字符的数量应该是相同的。我们可以使用数组或哈希表来统计字符出现的次数。
具体算法:
- 创建两个计数数组
pCount
和sCount
,分别统计p
和当前窗口中每个字符的出现次数。 - 使用滑动窗口遍历字符串
s
:- 初始化窗口大小为
p
的长度。 - 移动窗口时,更新窗口内字符的计数。
- 比较两个计数数组,如果相同,则当前窗口起始位置是
p
的字母异位词的起始索引。
- 初始化窗口大小为
4.2 Java代码实现
public class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> result = new ArrayList<>();
if (s == null || p == null || s.length() < p.length()) {
return result;
}
int sLen = s.length();
int pLen = p.length();
// 创建两个计数数组
int[] pCount = new int[26]; // 假设只包含小写字母
int[] sCount = new int[26];
// 统