Skip to content

Commit 4d59e41

Browse files
committed
Minimum Window Substring, Multiply Strings
1 parent f5f3a4d commit 4d59e41

File tree

3 files changed

+198
-7
lines changed

3 files changed

+198
-7
lines changed

C++/LeetCodet题解(C++版).pdf

21.6 KB
Binary file not shown.

C++/chapDynamicProgramming.tex

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -513,13 +513,18 @@ \subsubsection{描述}
513513

514514

515515
\subsubsection{分析}
516-
首先想到的是递归(即深搜),对两个string进行分割,然后比较四对字符串。代码虽然简单,但是复杂度比较高。有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,和动规中自顶向下的记忆化搜索类似
516+
首先想到的是递归(即深搜),对两个string进行分割,然后比较四对字符串。代码虽然简单,但是复杂度比较高。有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,即memorization(翻译为记忆化搜索)
517517

518518
剪枝可以五花八门,要充分观察,充分利用信息,找到能让节点提前返回的条件。例如,判断两个字符串是否互为scamble,至少要求每个字符在两个字符串中出现的次数要相等,如果不相等则返回false。
519519

520520
加缓存,可以用数组或HashMap。本题维数较高,用HashMap,\fn{std::map}和\fn{std::unordered_map}均可。
521521

522-
其次,这题可以用动规。
522+
既然可以用记忆化搜索,这题也一定可以用动规。设状态为\fn{f[i][j][k]},表示长度为$i$,起点为\fn{s1[j]}和起点为\fn{s2[]}两个字符串是否互为scramble,则状态转移方程为
523+
\begin{Code}
524+
f[i][j][k]} = (f[s][j][k] && f[i-s][j+s][k+s])
525+
|| (f[s][j][k-s] && f[i-s][j+s][k])
526+
\end{Code}
527+
523528

524529
\subsubsection{代码}
525530

@@ -681,10 +686,6 @@ \subsubsection{代码}
681686
};
682687
\end{Code}
683688

684-
\begin{Code}
685-
686-
\end{Code}
687-
688689

689690
\subsubsection{相关题目}
690691
\begindot

C++/chapImplement.tex

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,194 @@ \subsubsection{相关题目}
184184

185185
\begindot
186186
\item Insert Interval,见 \S \ref{sec:insert-interval}
187-
\myenddot
187+
\myenddot
188+
189+
190+
\section{Minimum Window Substring} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191+
\label{sec:minimum-window-substring}
192+
193+
194+
\subsubsection{描述}
195+
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)$.
196+
197+
For example, \code{S = "ADOBECODEBANC", T = "ABC"}
198+
199+
Minimum window is \code{"BANC"}.
200+
201+
Note:
202+
\begindot
203+
\item If there is no such window in $S$ that covers all characters in $T$, return the emtpy string \code{""}.
204+
\item If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in $S$.
205+
\myenddot
206+
207+
208+
\subsubsection{分析}
209+
双指针,动态维护一个区间。尾指针不断往后扫,当扫到有一个窗口包含了所有$T$的字符后,然后再收缩头指针,直到不能再收缩为止。最后记录所有可能的情况中窗口最小的
210+
211+
212+
\subsubsection{代码}
213+
\begin{Code}
214+
// LeetCode, Minimum Window Substring
215+
// 双指针,动态维护一个区间。尾指针不断往后扫,当扫到有一个窗口包含了所有T的字符
216+
// 后,然后再收缩头指针,直到不能再收缩为止。最后记录所有可能的情况中窗口最小的
217+
class Solution {
218+
public:
219+
string minWindow(string S, string T) {
220+
if (S.empty()) return "";
221+
if (S.size() < T.size()) return "";
222+
223+
const int ASCII_MAX = 256;
224+
int appeared_count[ASCII_MAX];
225+
int expected_count[ASCII_MAX];
226+
std::fill(appeared_count, appeared_count + ASCII_MAX, 0);
227+
std::fill(expected_count, expected_count + ASCII_MAX, 0);
228+
229+
for (size_t i = 0; i < T.size(); i++) expected_count[T[i]]++;
230+
231+
int minWidth = INT_MAX, min_start = 0; // 窗口大小,起点
232+
int wnd_start = 0;
233+
int appeared = 0; // 完整包含了一个T
234+
//尾指针不断往后扫
235+
for (size_t wnd_end = 0; wnd_end < S.size(); wnd_end++) {
236+
if (expected_count[S[wnd_end]] > 0) { // this char is a part of T
237+
appeared_count[S[wnd_end]]++;
238+
if (appeared_count[S[wnd_end]] <= expected_count[S[wnd_end]])
239+
appeared++;
240+
}
241+
if (appeared == T.size()) { // 完整包含了一个T
242+
// 收缩头指针
243+
while (appeared_count[S[wnd_start]] > expected_count[S[wnd_start]]
244+
|| expected_count[S[wnd_start]] == 0) {
245+
appeared_count[S[wnd_start]]--;
246+
wnd_start++;
247+
}
248+
if (minWidth > (wnd_end - wnd_start + 1)) {
249+
minWidth = wnd_end - wnd_start + 1;
250+
min_start = wnd_start;
251+
}
252+
}
253+
}
254+
255+
if (minWidth == INT_MAX) return "";
256+
else return S.substr(min_start, minWidth);
257+
}
258+
};
259+
\end{Code}
260+
261+
262+
\subsubsection{相关题目}
263+
264+
\begindot
265+
\item
266+
\myenddot
267+
268+
269+
\section{Multiply Strings} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
270+
\label{sec:multiply-strings}
271+
272+
273+
\subsubsection{描述}
274+
Given two numbers represented as strings, return multiplication of the numbers as a string.
275+
276+
Note: The numbers can be arbitrarily large and are non-negative.
277+
278+
279+
\subsubsection{分析}
280+
281+
282+
283+
\subsubsection{代码}
284+
\begin{Code}
285+
/** 大整数. */
286+
class BigInt {
287+
public:
288+
/**
289+
* @brief 构造函数,将字符串转化为大整数.
290+
* @param[in] s 输入的字符串
291+
* @return 无
292+
*/
293+
BigInt(string s) {
294+
vector<int> result;
295+
result.reserve(s.size() / RADIX_LEN + 1);
296+
297+
for (int i = s.size(); i > 0; i -= RADIX_LEN) { // [i-RADIX_LEN, i)
298+
int temp = 0;
299+
const int low = max(i - RADIX_LEN, 0);
300+
for (int j = low; j < i; j++) {
301+
temp = temp * 10 + s[j] - '0';
302+
}
303+
result.push_back(temp);
304+
}
305+
n = result;
306+
}
307+
/**
308+
* @brief 将整数转化为字符串.
309+
* @return 字符串
310+
*/
311+
string toString() {
312+
stringstream result;
313+
bool started = false; // 用于跳过前导0
314+
for (auto i = n.rbegin(); i != n.rend(); i++) {
315+
if (started) { // 如果多余的0已经都跳过,则输出
316+
result << std::setw(4) << std::setfill('0') << *i;
317+
} else {
318+
result << *i;
319+
started = true; // 碰到第一个非0的值,就说明多余的0已经都跳过
320+
}
321+
}
322+
323+
if (!started) return "0"; // 当x全为0时
324+
else return result.str();
325+
}
326+
327+
/**
328+
* @brief 大整数乘法.
329+
* @param[in] x x
330+
* @param[in] y y
331+
* @return 大整数
332+
*/
333+
static BigInt multiply(const BigInt &x, const BigInt &y) {
334+
vector<int> z(x.n.size() + y.n.size() + 1, 0);
335+
336+
for (size_t i = 0; i < y.n.size(); i++) {
337+
for (size_t j = 0; j < x.n.size(); j++) { // 用y[i]去乘以x的各位
338+
// 两数第i, j位相乘,累加到结果的第i+j位
339+
z[i + j] += y.n[i] * x.n[j];
340+
341+
if (z[i + j] >= BIGINT_RADIX) { // 看是否要进位
342+
z[i + j + 1] += z[i + j] / BIGINT_RADIX; // 进位
343+
z[i + j] %= BIGINT_RADIX;
344+
}
345+
}
346+
}
347+
while (z.back() == 0) z.pop_back(); // 没有进位,去掉最高位的0
348+
return BigInt(z);
349+
}
350+
351+
private:
352+
/** 一个数组元素表示4个十进制位,即数组是万进制的 */
353+
const static int BIGINT_RADIX = 10000;
354+
const static int RADIX_LEN = 4;
355+
/** 万进制整数. */
356+
vector<int> n;
357+
BigInt(const vector<int> num) : n(num) {}
358+
};
359+
360+
361+
// LeetCode, Multiply Strings
362+
class Solution {
363+
public:
364+
string multiply(string num1, string num2) {
365+
BigInt x(num1);
366+
BigInt y(num2);
367+
return BigInt::multiply(x, y).toString();
368+
}
369+
};
370+
\end{Code}
371+
372+
373+
\subsubsection{相关题目}
374+
375+
\begindot
376+
\item
377+
\myenddot

0 commit comments

Comments
 (0)