[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)); }}
- [Leetcode]Substring with Concatenation of All Words & Minimum Window Substring
- LeetCode: Substring with Concatenation of All Words
- LeetCode : Substring with Concatenation of All Words
- [Leetcode] Substring with Concatenation of All Words
- [LeetCode]Substring with Concatenation of All Words
- LeetCode-Substring with Concatenation of All Words
- [LeetCode] Substring with Concatenation of All Words
- LeetCode:Substring with Concatenation of All Words
- Leetcode: Substring with Concatenation of All Words
- [LeetCode] Substring with Concatenation of All Words
- leetcode Substring with Concatenation of All Words
- leetcode Substring with Concatenation of All Words
- LeetCode Substring with Concatenation of All Words
- LeetCode | Substring with Concatenation of All Words
- LeetCode - Substring with Concatenation of All Words
- Substring with Concatenation of All Words -- LeetCode
- Leetcode: Substring with Concatenation of All Words
- leetcode:Substring with Concatenation of All Words
- React Native在直播应用中的实践
- python踩过的坑之py文件命名导致导入包错误
- iOS容易闪退的bug
- Android设计登录界面、找回密码、注册功能
- 【Android 仿微信通讯录 导航分组列表-下】自定义View为RecyclerView打造右侧索引导航栏IndexBar
- [Leetcode]Substring with Concatenation of All Words & Minimum Window Substring
- 关于方法的长度
- android SharedPreferences设置初始密码,并修改密码
- 10个小计划,提高java学习效率
- drop,delete和truncate的区别
- VS2008创建C++项目
- Ubuntu Navicat for MySQL安装以及破解方案
- 初始node.js
- Python使用struct处理二进制(pack和unpack用法)