[LeetCode] 727. Minimum Window Subsequence 最小窗口子序列
Given strings S
and T
, find the minimum (contiguous) substring W
of S
, so that T
is a subsequenceof W
.
If there is no such window in S
that covers all characters in T
, return the empty string ""
. If there are multiple such minimum-length windows, return the one with the left-most starting index.
Example 1:
Input: S = "abcdebdde", T = "bde" Output: "bcde" Explanation: "bcde" is the answer because it occurs before "bdde" which has the same length. "deb" is not a smaller window because the elements of T in the window must occur in order.
Note:
- All the strings in the input will only contain lowercase letters.
- The length of
S
will be in the range[1, 20000]
. - The length of
T
will be in the range[1, 100]
.
给定字符串S和T,在S中寻找最小连续子串W,使得T是W的子序列。如果没有找到返回"",如果找到多个最小长度的子串,返回左 index 最小的。
解法1:暴力搜索brute force,对于每一个s[i],从s[0]到s[i]扫描,看是否按顺序满足目标字符。 显然要超时,不是题目要求的。
解法2: 动态规划DP, 二维数组dp[i][j]表示T[0...i]在S中找到的起始下标index,使得S[index, j]满足目前T[0...i]。首先找到能满足满足T中第一个字符T[0]的S中的字符下标存入dp[0][j],也就是满足第一个字符要求一定是从这些找到的字符开始的。然后在开始找第二个字符T[1],扫到的字符dp[j]存有index,说明可以从这里记录的index开始,找到等于T[1]的S[j]就把之前那个index存进来,说明从这个index到j满足T[0..1],一直循环,直到T中的i个字符找完。如果此时dp[i][j]中有index,说明S[index, j]满足条件,如有多个输出最先找到的。
State: dp[i][j],表示在S中找到的起始下标 index ,使得 S[index...j] 满足目前 T[0...i] 是其子序列。
function: dp[i+1][k] = dp[i][j] if S[k] = T[i+1] , 如果查看到第i+1行(也就是第 T[i+1] 的字符),如果满足S[k] = T[i+1],就把上一行找到的index赋给它。
Initialize: dp[0][j] = j if S[j] = T[0] , 二维数组的第一行,如果字符S[j] = T[0], 就把S[j]的index(就是j)付给它。其他元素均为 None 或者 -1。
Return: dp[len(T) - 1][j], if dp[len(T) - 1][j] != None, 返回最小的。如果没有返回 ""
由于我们只用到前一行的值,所以可以只用2行的二维数组,每一个循环更新其中的一行。可以用 j % 2 来往复使用。
Java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Solution { public String minWindow(String S, String T) { int m = T.length(), n = S.length(); int [][] dp = new int [m + 1 ][n + 1 ]; for ( int j = 0 ; j <= n; j++) { dp[ 0 ][j] = j + 1 ; } for ( int i = 1 ; i <= m; i++) { for ( int j = 1 ; j <= n; j++) { if (T.charAt(i - 1 ) == S.charAt(j - 1 )) { dp[i][j] = dp[i - 1 ][j - 1 ]; } else { dp[i][j] = dp[i][j - 1 ]; } } } int start = 0 , len = n + 1 ; for ( int j = 1 ; j <= n; j++) { if (dp[m][j] != 0 ) { if (j - dp[m][j] + 1 < len) { start = dp[m][j] - 1 ; len = j - dp[m][j] + 1 ; } } } return len == n + 1 ? "" : S.substring(start, start + len); } } |
Java: brute force, Time O(s*t), Space O(s*t)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Solution { public String minWindow(String S, String T) { int min = - 1 , idx = - 1 ; char [] Tc = T.toCharArray(); char [] Sc = S.toCharArray(); for ( int i = 0 ;i < S.length();i++){ if (Sc[i] != Tc[ 0 ]) continue ; int len = check(Tc,Sc,i); if (len <= 0 ) break ; if (min == - 1 || len < min){ idx = i; min = len; } } if (min == - 1 ) return "" ; return S.substring(idx, idx + min); } public int check( char [] Tc, char [] Sc, int start){ int i = start, j = 0 ; while (i < Sc.length && j < Tc.length){ if (Sc[i] == Tc[j]) j++; i++; } if (j == Tc.length) return i - start; return - 1 ; } } |
Java: DP, Time O(s*t), Space O(s*2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Solution { public String minWindow(String S, String T) { int [][] dp = new int [ 2 ][S.length()]; for ( int i = 0 ; i < S.length(); ++i) dp[ 0 ][i] = S.charAt(i) == T.charAt( 0 ) ? i : - 1 ; for ( int j = 1 ; j < T.length(); ++j) { int last = - 1 ; Arrays.fill(dp[j & 1 ], - 1 ); for ( int i = 0 ; i < S.length(); ++i) { if (last >= 0 && S.charAt(i) == T.charAt(j)) dp[j & 1 ][i] = last; if (dp[j & 1 ][i] >= 0 ) last = dp[j & 1 ][i]; } } int start = 0 , end = S.length(); for ( int e = 0 ; e < S.length(); ++e) { int s = dp[T.length() & 1 ][e]; if (s >= 0 && e - s < end - start) { start = s; end = e; } } return end < S.length() ? S.substring(start, end+ 1 ) : "" ; } } |
Java: Time O(s*t), Space O(s*t)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | class Solution { public String minWindow(String S, String T) { int [][] dp = new int [T.length()][S.length()]; for ( int i = 0 ; i < T.length(); i++) { for ( int j = 0 ; j < S.length(); j++) { dp[i][j] = - 1 ; } } for ( int j = 0 ; j < S.length(); j++) { dp[ 0 ][j] = (S.charAt(j) == T.charAt( 0 )) ? j : - 1 ; } for ( int i = 1 ; i < T.length(); i++) { int last = - 1 ; for ( int j = 0 ; j < S.length(); j++) { if (last >= 0 && S.charAt(j) == T.charAt(i)) { dp[i][j] = last; } if (dp[i - 1 ][j] >= 0 ) { last = dp[i - 1 ][j]; } } } int start = - 1 ; int length = Integer.MAX_VALUE; for ( int j = 0 ; j < S.length(); j++) { if (dp[T.length() - 1 ][j] >= 0 && (j - dp[T.length() - 1 ][j] + 1 < length)) { start = dp[T.length() - 1 ][j]; length = j - dp[T.length() - 1 ][j] + 1 ; } } return (start == - 1 ) ? "" : S.substring(start, start + length); } } |
Python: Time O(s*t), Space O(s*2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class Solution( object ): def minWindow( self , S, T): dp = [[ None for _ in xrange ( len (S))] for _ in xrange ( 2 )] for j, c in enumerate (S): if c = = T[ 0 ]: dp[ 0 ][j] = j for i in xrange ( 1 , len (T)): prev = None dp[i % 2 ] = [ None ] * len (S) for j, c in enumerate (S): if prev is not None and c = = T[i]: dp[i % 2 ][j] = prev if dp[(i - 1 ) % 2 ][j] is not None : prev = dp[(i - 1 ) % 2 ][j] start, end = 0 , len (S) for j, i in enumerate (dp[( len (T) - 1 ) % 2 ]): if i > = 0 and j - i < end - start: start, end = i, j return S[start:end + 1 ] if end < len (S) else "" |
Python:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | class Solution( object ): def minWindow( self , S, T): """ :type S: str :type T: str :rtype: str """ ans = '' ls, lt = len (S), len (T) dp = [ - 1 ] * lt for x in range (ls): for y in range (lt - 1 , - 1 , - 1 ): if T[y] = = S[x]: dp[y] = dp[y - 1 ] if y else x if y = = lt - 1 and dp[ - 1 ] > - 1 : nlen = x - dp[ - 1 ] + 1 if not ans or nlen < len (ans): ans = S[dp[ - 1 ] : x + 1 ] return ans |
C++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | /* * At time j, for each position e in S (e for end), let's remember * the largest index cur[e] = s (for start) so that S[s: e+1] has * T[:j+1] as a subsequence, and -1 otherwise if it isn't possible. */ class Solution { public : string minWindow(string S, string T) { int sn = S.size(), tn = T.size(); vector< int > memo(sn, -1); for ( int i = 0; i < sn; ++i) { if (T[0] == S[i]) { memo[i] = i; } } for ( int j = 1; j < tn; ++j) { vector< int > swap(sn, -1); int currStart = -1; for ( int i = 0; i < sn; ++i) { if (S[i] == T[j] && currStart >= 0) { // T[:j+1] found swap[i] = currStart; } if (memo[i] >= 0) { currStart = memo[i]; } } std::swap(memo, swap); } int BAR = sn + 1, minLen = BAR; int start = 0; for ( int e = 0; e < sn; ++e){ if (memo[e] >= 0) { int currLen = e + 1 - memo[e]; if (currLen < minLen) { start = memo[e]; minLen = currLen; } } } return minLen == BAR ? "" : S.substr(start, minLen); } }; |
C++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Solution { public : string minWindow(string s, string t) { int ns = s.size(), nt= t.size(); int dp[ns+1][nt+1] = {}; const int mxx = ns + 1; //for(int i=0;i<=ns;i++) dp[i][0]=i; for ( int i = 0 ; i <= ns; ++i) { for ( int j = 1; j <= nt; ++j) { dp[i][j] = mxx; if (i) { dp[i][j] = min(dp[i][j], 1 + dp[i-1][j]); if (s[i-1] == t[j-1]) dp[i][j] = min(dp[i][j], 1 + dp[i-1][j-1]); } } } int ans = ns + 1, x = -1; for ( int i = 0; i <=ns; ++i) if (dp[i][nt] < ans) { x = i; ans = dp[i][nt]; } if (x < 0) return "" ; return s.substr(x-ans,ans); } }; |
类似题目:
[LeetCode] 3.Longest Substring Without Repeating Characters 最长无重复子串
[LeetCode] 76. Minimum Window Substring 最小窗口子串
All LeetCode Questions List 题目汇总
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架