[Leetcode]Substring with Concatenation of All Words & Minimum Window Substring

来源:互联网 发布:jvm垃圾回收算法 编辑:程序博客网 时间:2024/06/05 19:32

这两道题有一定的联系和区别:这类寻找子序列组合的题目大都可以采取双指针+Map的思想进行处理。


Substring with Concatenation of All Words中,给定了一些等长的单词words,要求在指定字符串s中找出这些单词的连续组合序列;

Minimum Window Substring中,给定了一个子串t,要求在指定字符串s中找出子串t中所有字母所构成的最短子序列,可以理解为原来的单词更改为字母了(字母肯定是等长的啊=。=)。

显然第二道题目放宽了要求,并不需要保证t中所有字母连续按顺序出现。

一、题目描述:

==========================================Substring with Concatenation of All Words======================================

You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

For example, given:
s"barfoothefoobarman"
words["foo", "bar"]

You should return the indices: [0,9].
(order does not matter).

================================================Minimum Window Substring===========================================

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

For example,
S = "ADOBECODEBANC"
T = "ABC"

Minimum window is "BANC".

Note:
If there is no such window in S that covers all characters in T, return the empty string "".

If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.


二、解决思路:


=====================================Substring with Concatenation of All Words=========================================


题目是给定等长的一组字符串words,要在指定字符串s中找出这些words连续出现的位置,只要满足他们的任意排列即可,不过一定要保证连续和数量一致。

我们记指定字符串s的长度为sLen,每个word的长度一致记为wordLen。我们采用滑动窗口的策略(即双指针构成一个窗口),窗口的大小windowSize即为words数组的大小*wordLen,窗口的起始位置记为startPos。

因为任何一个满足要求的字串必然与windowSize大小一致,并且各个word所出现次数必然与words数组中一致,这就是我们对这个子串的要求,有了这两个要求,就比较清楚思路了。


           首先,采用一个HashMap来记录每个words[i]在words中出现的次数(这里严重吐槽一下Java对map操作之麻烦。。。),记为map。

           然后,采用一个技巧,虽然一般的字符串匹配常常采用KMP、BM等方法,但是这里由于有了每个单词的大小wordLen,我们借用这个特点,只需对指定字符串s的起始位置从外层指针i=0进行wordLen次尝试即可。每次尝试的时候用一个HashMap记录窗口中各个单词出现的次数,记为appearMap。用一个内层指针j对s字符串取wordLen长度的子串tmp,在原map中查询其是否出现过:

           i. 如果子串tmp在map中出现过,将其在appearMap中出现的次数加一。

              

              如果此时appearMap[tmp]的次数小于map[tmp],说明该子串可以加入当前备选序列;

              如果当前appearMap[tmp]的次数大于等于map[tmp],说明子串tmp已重复出现多次,相当于一个错误word的出现,但是此时不需要将之前的匹配完成的子串全部移除,而是采用窗口前移的方式,移到当前窗口中tmp出现的第一个位置将这个tmp移出窗口即可。注意此过程中appearMap值的变化!


              然后判断此时所有words是不是都在appearMap出现了且次数与map所要求的相同,如果是,则记录下此时startPos的值。

          ii. 如果子串tmp没有在map中出现过,说明这是一个完全错误的word,此时直接将appearMap清空,窗口起点startPos移动到此时tmp之后的位置。



================================================Minimum Window Substring=========================================


做完前一道题后再做这道,容易惯性的想到也用两个map去做索引和比较,当然这样也是可以的,只不过对于未出现的字母不再做错误处理。

这里发现一个比较好的方法:

首先,还像前一道题目一样用map去记录字符串t中各个字母的出现次数。

然后,采用两个指针start和end,分别指向子序列的起始位置和终止位置,初始均为0,并用一个计数器cnt来计算当前子序列中还需要多少个t中的字母(初始化为t的长度)。在end到达s末端之前执行如下循环操作:

        如果s[end]在map中,将其map[s[end]]减一,如果map[s[end]]已经小于0,说明子序列中已经包含过多的s[end]字母,此时不再对cnt进行减一操作,其他情况需对cnt--。end指针后移一位。

        对cnt进行判断,如果cnt为0,说明此时s[start,end)序列中已包含所有字母,在此情况下进行以下循环操作:

                 判断当前序列长度是否小于之前找到的序列的长度,如果是则更新。
                 判断此时s[start]是否是t中的字母,如果是则将map[s[start]]++(因为接下来要将s[start]移出序列了),如果此时map[s[start]]大于0,说明移出的s[start]在当前子序列中没有多余的相同字母,故此时要将cnt++(该操作与之前cnt--操作放在一起理解)。

                 指针start前移一位,注意!很多人在用前一道题的方法解决这道题的时候不知道如何处理start的前移,感觉要再搜一遍map,这里可以通过cnt==0的循环条件控制start前移(虽然每次只能前移一位=。=)

        循环结束

循环结束


三、源码:

========================================Substring with Concatenation of All Words=========================================

import java.util.*;import java.io.*;public class SubstringWithConcatenation {public List<Integer> findSubstring(String s, String[] words) {        List<Integer> li = new ArrayList<Integer>();        if (words.length == 0 || s.length() == 0) return li;        int sLen = s.length(), wordLen = words[0].length();        int windowSize = words.length*wordLen;        Map<String, Integer> map = new HashMap<>();        for (int i = 0; i < words.length; i++) {if (map.containsKey(words[i])) {map.put(words[i], map.get(words[i])+1);}else map.put(words[i], 1);}        for (int i = 0; i < wordLen; i++) {int startPos = i, cnt = 0;Map<String, Integer> appearMap = new HashMap<>();for (int j = i; j+wordLen <= s.length(); j += wordLen) {String tmp = s.substring(j, j+wordLen);if (map.containsKey(tmp)) {if (appearMap.containsKey(tmp)) {appearMap.put(tmp, appearMap.get(tmp)+1);}else appearMap.put(tmp, 1);if (appearMap.get(tmp) <= map.get(tmp)) {cnt++;}else {while (appearMap.get(tmp) > map.get(tmp)) {String front = s.substring(startPos, startPos+wordLen);appearMap.put(front, appearMap.get(front)-1);if (appearMap.get(front) < map.get(front)) {cnt--;}startPos += wordLen;}}if (cnt == words.length) {li.add(startPos);String front = s.substring(startPos, startPos+wordLen);startPos += wordLen;cnt--;appearMap.put(front, appearMap.get(front)-1);}}else {appearMap.clear();cnt = 0;startPos = j+wordLen;}}}        return li;    }public static void main(String[] args) {SubstringWithConcatenation substringWithConcatenation = new SubstringWithConcatenation();Scanner scanner = new Scanner(System.in);String string= scanner.nextLine();String[] words = new String[Integer.parseInt(args[0])];for (int i = 0; i < Integer.parseInt(args[0]); i++) {words[i] = scanner.nextLine();}List<Integer> list = substringWithConcatenation.findSubstring(string, words);for (Integer integer : list) {System.out.println(integer);}}}


=============================================Minimum Window Substring==================================================

import java.io.*;import java.util.*;public class MinimumWindowSubstring {public String minWindow(String s, String t) {        if (s.length() < t.length()) return "";        Map<Character, Integer> map = new HashMap<>();        for (int i = 0; i < t.length(); i++) {        map.put(t.charAt(i), map.getOrDefault(t.charAt(i), 0)+1);        }        int end=0, start=0, minDist=Integer.MAX_VALUE, cnt=t.length();        String ans = "";        while (end < s.length()) {        char curr = s.charAt(end);        if (map.containsKey(curr)) {        int currValue = map.get(curr);        map.put(curr, currValue-1);        if (currValue > 0) {        cnt--;        }        }        end++;        while (cnt == 0) {        if (end-start < minDist) {        minDist = end-start;        ans = s.substring(start, end);        }        char front = s.charAt(start);        if (map.containsKey(front)) {        int frontValue = map.get(front)+1;        map.put(front, frontValue);        if (frontValue > 0) {        cnt++;        }        }        start++;        }        }        return ans;    }    public static void main(String[] args) {    MinimumWindowSubstring mw = new MinimumWindowSubstring();    Scanner scan = new Scanner(System.in);    String s = scan.nextLine();    String t = scan.nextLine();    System.out.println(mw.minWindow(s, t));    }}


0 0
原创粉丝点击