本章包括的题目有:
438. 找到字符串中所有字母异位词 - 力扣(LeetCode)
1.无重复字符的最长子串
思路解析:
要在一个字符串中找出最长的不含重复字符的子串,只需维护一个内部无重复的"窗口",在遍历过程中不断扩展并调整这个窗口,同时记录它曾经达到的最大长度。由于字符集有限(题目使用标准 ASCII,共 128 个),可以用一个长度为 128 的计数数组来快速判断当前窗口内某个字符是否重复出现。
算法使用左右指针 l 和 r 表示窗口的边界。一开始两者都在起始位置,计数数组全为零。右指针 r 依次向右移动,每次将新进入窗口的字符 s[r] 的计数加 1。如果该字符的计数超过 1,说明这个字符在窗口内重复了(由于只有新加入的字符可能造成重复),此时就不断右移左指针 l,并把离开窗口的字符计数减 1,直到该重复字符的计数降回 1,窗口重新成为无重复子串。每得到一个无重复的窗口,就用它的长度 r - l + 1 更新历史最大值 ret。当右指针遍历完整个字符串,ret 就是最长无重复子串的长度。
代码实现:
java
class Solution {
public int lengthOfLongestSubstring(String s) {
int ret = 0;
int len = s.length();
int l = 0,r = 0;
int[] arr = new int[128];
for (;r < len; r++) {
arr[s.charAt(r)]++;
while(arr[s.charAt(r)] > 1){
arr[s.charAt(l)]--;
l ++;
}
ret = Math.max(ret, (r - l + 1));
}
return ret;
}
}
时间复杂度O(n)
空间复杂度O(1)
2.找到字符串中所有字母异位词
思路解析:
要在字符串 s 中找出所有与 p 互为字母异位词的子串的起始索引,只需用固定大小的滑动窗口在 s 上移动,并比较窗口内字符的频数与 p 的频数是否相同。由于题目只涉及小写字母,字符集大小固定为 26,可以用两个长度为 26 的整数数组分别记录 p 的字符频数和当前窗口的字符频数。
首先统计 p 中每个字母的出现次数,存入 count。然后初始化一个长度为 |p| 的窗口,但预先只将前 |p|-1 个字符的计数填入 count2,这样后续循环中就能自然地完成"加入右边界 → 比较 → 移除左边界"的固定节奏。若 count2 与 count 完全相等(使用 Arrays.equals 比较),说明当前窗口内的子串是 p 的一个字母异位词,把窗口的起始索引 i - |p| + 1 加入结果列表。比较完成后,将窗口左边界字符(同样是 i - |p| + 1)的计数减 1,为下一轮滑动做好准备。遍历结束,结果列表即为所有异位词子串的起始位置。
代码实现:
java
class Solution {
public List<Integer> findAnagrams(String s, String p) {
if(p.length() > s.length())return new ArrayList<>();
List<Integer> ret = new ArrayList<>();
char[] pp = p.toCharArray();
int[] count = new int[26];
for(char c: pp){
count[c - 'a'] ++;
}
int[] count2 = new int[26];
for (int i = 0; i < p.length() - 1; i++) {
count2[s.charAt(i) - 'a']++;
}
for (int i = p.length() - 1; i < s.length(); i++) {
count2[s.charAt(i) - 'a']++;
if(Arrays.equals(count,count2))ret.add(i - p.length() + 1);
count2[s.charAt(i - p.length() + 1) - 'a'] --;
}
return ret;
}
}
补充:Arrays.equals(count,count2) 可以比较两个数组是否相同,但是 count.equals(count2) 只能比较两者的内存地址(引用)
时间复杂度O(n)
空间复杂度O(1)
上一章: