ag-滑动窗口

ag-滑动窗口

[3] 无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

分析:

    a  b  c  a  b  c  b  b
    
    |a  b  c  a  b  c  b  b
    ↑ left=0,maxlen=1 窗口范围 [a]
    a  |b  c  a  b  c  b  b
    ↑ left=0,maxlen=2 窗口范围 [ab]
    a  b  |c  a  b  c  b  b
    ↑ left=0,maxlen=3 窗口范围 [abc]
    a  b  c  |a  b  c  b  b
       ↑ left=1,maxlen=3 窗口范围 [bca]

代码:

public int lengthOfLongestSubstring(String s) {
    HashMap<Character, Integer> map = new HashMap<>();
    int maxlen = 0;
    int left = 0;

    for (int i = 0; i < s.length(); i++) {
        if (map.containsKey(s.charAt(i))) {
            left = Math.max(left, map.get(s.charAt(i)) + 1);
        }
        map.put(s.charAt(i), i);
        maxlen = Math.max(maxlen, i - left + 1);
    }
    return maxlen;
}

[30] 串联所有单词的子串
给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。
例如,如果 words = ["ab","cd","ef"], 那么 abcdef、abefcd、cdabef、cdefab、efabcd、efcdab 都是串联子串。 "acdbef" 不是串联子串,因为他不是任何 words 排列的连接。返回所有串联字串在 s 中的开始索引。你可以以 任意顺序 返回答案。

输入:s = "barfoothefoobarman", words = ["foo","bar"]
输出:[0,9]
解释:因为 words.length == 2 同时 words[i].length == 3,连接的子字符串的长度必须为 6。
子串 "barfoo" 开始位置是 0。它是 words 中以 ["bar","foo"] 顺序排列的连接。
子串 "foobar" 开始位置是 9。它是 words 中以 ["foo","bar"] 顺序排列的连接。
输出顺序无关紧要。返回 [9,0] 也是可以的。

分析:

>>> barfoothefoobarman
1、单词个数m=6,长度n=3,字符串长度ls=18
2、按照长度(n=3)的维度进行遍历
    bar  foo  the  foo  bar  man
1) [barfoo] = bar foo
   [foothe] = foo the  ...
2) [arfoot] = arf oot
   [oothef] = oot hef ...
3) [rfooth] = rfo oth
   [othefo] = oth efo ...
3、Map<String, Integer> differ
  出现在窗口中的单词,每出现一次,则+1。
  出现在words中的单词,每出现一次,则-1。如果value为0,则移除。
4、窗口右移。窗口移动时,若出现 \textit{differ}differ 中值不为 00 的键的数量为 00,则表示这个窗口中的单词频次和 words 中单词频次相同,窗口的左端点是一个待求的起始位置。

代码:

public static List<Integer> findSubstring2(String s, String[] words) {
    List<Integer> res = new ArrayList<Integer>();
    int m = words.length;
    int n = words[0].length();
    int ls = s.length();
    for (int i = 0; i < n; i++) {// 即不同的起始位置
        if (i + n * m > ls) {// 长度不足直接退出循环
            break;
        }
        Map<String, Integer> differ = new HashMap<>();
        // 在窗口范围内统计
        for (int j = 0; j < m; j++) {
            // 窗口范围内截取单词
            String word = s.substring(i + j * n, i + (j + 1) * n);
            differ.put(word, differ.getOrDefault(word, 0) + 1);
        }
        for (String word : words) {
            differ.put(word, differ.getOrDefault(word, 0) - 1);
            if (0 == differ.get(word)) {
                differ.remove(word);
            }
        }
        for (int start = i; start < ls - m * n + 1; start += n) {
            if (start != i) {
                // 右侧移入n,新进入的单词
                String word = s.substring(start + (m - 1) * n, start + m * n);
                differ.put(word, differ.getOrDefault(word, 0) + 1);
                if (0 == differ.get(word)) {
                    differ.remove(word);
                }
                // 左侧移除n,移除的单词
                word = s.substring(start - n, start);
                differ.put(word, differ.getOrDefault(word, 0) - 1);
                if (differ.get(word) == 0) {
                    differ.remove(word);
                }
            }
            if (differ.isEmpty()) {
                res.add(start);
            }
        }
    }
    return res;
}

[76] 最小覆盖子串

评论

暂无

添加新评论