Leetcodee 76. 最小覆蓋子串 雙指針 哈希

地址 https://leetcode-cn.com/problems/minimum-window-substring/

給你一個字符串 s 、一個字符串 t 。
返回 s 中涵蓋 t 所有字符的最小子串。
如果 s 中不存在涵蓋 t 所有字符的子串,則返回空字符串 "" 。

注意:
對於 t 中重複字符,我們尋找的子字符串中該字符數量必須不少於 t 中該字符數量。
如果 s 中存在這樣的子串,我們保證它是唯一的答案。
 
示例 1:
輸入:s = "ADOBECODEBANC", t = "ABC"
輸出:"BANC"

示例 2:
輸入:s = "a", t = "a"
輸出:"a"

示例 3:
輸入: s = "a", t = "aa"
輸出: ""
解釋: t 中兩個字符 'a' 均應包含在 s 的子串中,
因此沒有符合條件的子字符串,返回空字符串。
 

提示:
1 <= s.length, t.length <= 105
s 和 t 由英文字母組成
 

進階:你能設計一個在 o(n) 時間內解決此問題的算法嗎?

解答
之前有談過滑動窗口雙指針的方法。
可以動態的擴大縮小連續的字符串,那麼我們可以使用該方法獲取最短長度滿足涵蓋目標字符串的子字符串。
如圖


確認使用雙指針後,還有一個問題需要解決,如何快速的判斷當前雙指針的範圍內的字符串是否涵蓋目標字符串。
可以使用哈希,快速的記錄和查詢當前字母是否包含在某段範圍內。
擴大範圍的時候 就把擴大包含的字母記錄到哈希表中
縮小範圍的時候 就把縮小拋棄的字母移出哈希表,或者說從哈希表中刪除
縮小範圍的操作

if (mm.count(s[l]) != 0) {
  //記錄減1
  checkm[s[l]]--;
}
//移動左指針 縮小範圍
l++;

擴大範圍的操作

r++;
if (r < s.size() && mm.count(s[r]) != 0) {
  checkm[s[r]]++;
}

全部代碼如下

class Solution {
public:
	unordered_map<char, int> mm;

	void init(const string& s) {
		for (auto& e : s) {
			mm[e]++;
		}
	}
	bool isEqual(unordered_map<char, int>& a, unordered_map<char, int>& b) {
		if (a.size() != b.size()) return false;

		for (auto& e : b) {
			if (e.second < a[e.first]) return false;
		}

		return true;
	}
	string minWindow(string s, string t) {
		init(t);
		int l = 0; int r = 0;
		unordered_map<char, int> checkm;
		if (mm.count(s[r]) != 0) {
			checkm[s[r]]++;
		}
		int ans = 999999; int ansl = -1; int ansr = -1;
		while (l < s.size() && r < s.size()) {
			if (isEqual(mm, checkm)) {
				//縮小範圍
				if (ans > (r - l + 1)) {
					ans = r - l + 1; ansl = l; ansr = r;
				}
				if (l <= r) {
					if (mm.count(s[l]) != 0) {
						checkm[s[l]]--;
					}
					l++;
				}

			}
			else {
				//擴大範圍
				r++;
				if (r < s.size() && mm.count(s[r]) != 0) {
					checkm[s[r]]++;
				}
			}
		}
		if (ansl == -1)return "";
		string ansstr = s.substr(ansl, ansr - ansl + 1);
		return ansstr;
	}
};

我的視頻題解空間

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章