給定一個字串S和字串T,要求找出S中包含T中所有字元的最小子串,不需要滿足順序,要求時間複雜度O(n)
如:
例子1:
String S =“XDOYEZODEYXNZ”;
String T =“XYZ” ;
返回 YXNZ
String S =“abcAbA”;
String T =“AA” ;
返回 AbA
傳統的方法是使用暴力解法,寫兩個雙層迴圈,但是這樣話,時間複雜度為 O(n2),n的平方了,而不是O(n)了
所以使用雙層迴圈肯定是不行了,這裡只能使用滑動視窗的思想來解決
滑動視窗的思想大概如下:
假設定義滑動視窗的左端為left、右端為right
1 先固定left,right=left,right右移動,並判斷字元是否包含在T中,包含則計數
2 當left和right之間剛好滿足包含T的所有字元時,找到了子串[left,right],但這個子串還不是最短的;
3 接下來就是縮短子串[left,right],改變left、right使當前子串縮短
因為是剛好找到滿足的條件,所以S[right]必定包含在T中,所以右端不能再變,也就是right不能變了,
只能移動left,由以下2種情況left是可以右移動的:
1)如果left對應的字元不包含在T中,說明該字元可以捨棄
2)如果left對應的字元包含在T中,但是該字串在子串中個數已經大於T中對應的字元的個數了,表明也可以捨棄,
比如子串中a有3個,T中a2個,那麼子串中減少一個a後,仍然滿足包含T的所有字元,所以這種情況也可以捨棄字元
使用while迴圈移動left,直到left對應的字元不滿足1)和2)為止,則此時是left和right就是當前的最小子串
4 比較當前的最小子串寬度是否大於前面找到的最小子串,如果大於,則使用第3步中的結果,否則捨棄第3步的結果
5 left右移一位,同時計數條件減1,重複 1、2、3、4的過程
因為此時的left的字元必定包含在T中,移動則當前子串丟失該字串,會導致包含條件不滿足了,所以計數條件要減一
單看我的描述可能理解會比較吃力,還是要看程式碼吧,裡面也有對應的註釋
程式碼如下:
import java。util。HashMap;import java。util。Map;/** * 使用滑動視窗思想 * 在S串中尋找包含字串T的所有字元的最小子串 * * 例如: S =“XDOYEZODEYXNZ” T =“XYZ”T=“XYZ” 找 出的最短子串為“YXNZ” * @author ssj * */public class MinStringWindow { public static void main(String[] args) { String S =“XDOYEZODEYXNZ”; String T =“XYZ” ; // String S =“abcAbA”;// String T =“AA” ; String min = minWindow(S,T); System。out。println(min); } /** * 在S字串中查詢包含T所有字元的最小子串 * @param S 原串 * @param T 目標串 * @return */ public static String minWindow(String S, String T) { if(S==null||T==null||S。length()0&&need。getOrDefault(c, 0)<=window。getOrDefault(c, 0)) { //跳過的是包含的字元,此時減少一個字元,條件就不滿足了,所以count要減一 need。put(c, need。getOrDefault(c, 0)-1); count——; } left++; } } } right++; } return width==Integer。MAX_VALUE?null:S。substring(minStart, minStart+width); }}
執行結果:
YXNZ