判斷字符串是否包含另一字符串的全排列

問題I:
給定兩個字符串A和B,判斷A中是否包含由B中字符重新排列成的新字符串。例如:A=abcdef, B=ba,結果應該返回true。因爲ba的排列ab,是A的子串。本問題來自:微信公衆賬號“待字閨中”。


分析:

設A的長度爲n,B的長度爲m

很直觀的做法是:

1)先分析B中的字符有有哪些,每種字符出現了多少次。

2)遍歷A中每個長度爲m的子串,統計字符,然後與B的統計結果對照。

這種解法雖然時間複雜度是O(2*n+m),但要做統計的對照,所以時間複雜度還與字符種類有關。若只有26個小寫字母,時間複雜度就是O(26*2n+m)。


實際上,這個係數可以優化掉:

用一個整數N表示B中字符的種類數;用整數M表示,在長度爲m的子串中出現次數不少於在B中出現次數的字符的種數。

遍歷A中每個長度爲m的子串時,當前子串的統計結果,只是在上一次統計結果的基礎上添加和去掉一個元素。

去掉一個字符ch,若去掉前,ch出現的次數正好等於在B中出現的次數,那麼--M。

添加一個字符ch,若添加後,ch出現的次數正好等於在B中出現的次數,那麼++M。

若M == N,那麼就在A中找到了一個B的全排列子串。

這樣,不論字符的總類有多少,時間複雜度總是O(2n+m)。

C++實現:

bool contin(const string &A, const string &B){
	if(A.size() < B.size())
		return false;
	if(B.empty() == true)
		return true;
	unordered_map<char, int> aMap, bMap;
	for(char ch : B)
		++bMap[ch];
	int kindCount = bMap.size();
	int loc = 0;
	int okCount = 0;
	while(okCount < kindCount && loc < A.size()){
		if(loc >= B.size()){
			int preHead = loc - B.size();
			int tmp = aMap[A[preHead]]--;
			auto it = bMap.find(A[preHead]);
			if(it != bMap.end() && tmp == it->second)
				--okCount;
		}
		int tmp = ++aMap[A[loc]];
		auto it = bMap.find(A[loc]);
		if(it != bMap.end() && tmp == it->second)
			++okCount;
		++loc;
	}
	return okCount == kindCount;
}



問題II:

將問題I變形一下,求A中最短的包含了B中所有字符的子串。

這其實是LeetCode上的一道題: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 emtpy string "".
If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S.

問題II的解法跟問題I很相似,就不再分析了,直接上代碼:

C++實現:時間複雜度同問題I

string minWindow(string A, string B) {
	if(A.size() < B.size() || B.empty())
		return "";
	unordered_map<char, int> aMap, bMap;
	for(char ch : B)
		++bMap[ch];
	int kindCount = bMap.size();
	int okCount = 0;
	int s = 0, t = 0;
	int minLen = INT_MAX;
	int resS = 0, resT = 0;
	while(t < A.size()){
		while(okCount != kindCount && t < A.size()){
			int tmp = ++aMap[A[t]];
			auto it = bMap.find(A[t]);
			if(it != bMap.end() && tmp == it->second)
				++okCount;
			++t;
		}
		while(okCount == kindCount){
			if(t - s < minLen){
				minLen = t - s;
				resS = s, resT = t;
			}
			int tmp = aMap[A[s]]--;
			auto it = bMap.find(A[s]);
			if(it != bMap.end() && tmp == it->second)
				--okCount;
			++s;
		}
	}
	return minLen == INT_MAX ? "" : A.substr(resS, resT - resS);
}




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