leetcode 87 擾亂字串

撫琴思伯牙發表於2021-04-16

image

題目意思就是說判斷s1能否經過題目所說的演算法得到s2。
題目中的演算法處理的位置為隨機,所以復現演算法基本無法得到s2
那麼只能搜尋了。


分析:

初始時:如果s1==s2,直接返回true。

  • 對於一個字串s1,在隨機位置分割s1,那麼我們只能遍歷s1,假設當前位置為分割處,然後進行後續操作,來判斷是否能得到s2。分割是同時對s1和s2都進行分割,對兩者子串進行比較。
  • 選取一個分割處後,假設分割後s1的兩字串為s1_left,s1_right。s2的為s2_left,s2_right。題目中演算法的處理僅為‘交換’,所以要想判斷s1是不是能經過變換得到s2,只需要如下等式
//注意,這是虛擬碼,實際上引數應該是根據起點和個數來切割得到的字串。
//因為切割得到的左右兩字串的size並不一定相等
isScramble(s1_left,s2_left)&&isScramble(s1_right,s2_right)//隨機決定,不交換
|| isScramble(s1_left,s2_right)&&isScramble(s1_right,s2_left)//隨機決定,交換
//兩者滿足其一即可,返回true

如果第一層遞迴遍歷所有切割點,都沒有找到,那麼返回false


初始程式碼


 class Solution {
 public:
	 bool isScramble(string s1, string s2) {
		 if (s1 == s2) return true;
		 int n = s1.size();
		 for (int i = 1; i < n; ++i)
		 {
			 string temp_s1_front, temp_s1_back, temp_s2_front, temp_s2_back,temp_s2_front_re,temp_s2_back_re;

			 temp_s1_front.assign(s1.begin(), s1.begin() + i);
			 temp_s1_back.assign(s1.begin() + i, s1.end());

			 temp_s2_front.assign(s2.begin(), s2.begin() + i);
			 temp_s2_back.assign(s2.begin() + i, s2.end());

			 temp_s2_front_re.assign(s2.end() - i, s2.end());
			 temp_s2_back_re.assign(s2.begin(), s2.end() - i);
			 if ( (isScramble(temp_s1_front, temp_s2_front)&&isScramble(temp_s1_back, temp_s2_back)) || (isScramble(temp_s1_front,temp_s2_front_re)&&isScramble(temp_s1_back,temp_s2_back_re)) )
			 {
				 return true;
			 }
		 }
		 return false;
	 }
 };

邏輯沒問題,答案也沒錯。但是嚴重超時。
image

剪枝!
s1想僅通過交換得到s2,那麼s1和s2裡面的字母必定是同一個集合,並且,每個字母的個數也必定是相同的。
那麼就很容易想到如下程式碼:

unordered_map<char, int> s1Map;
unordered_map<char, int> s2Map;
for (char c : s1)
{
	++s1Map[c];
}
for (char c : s2)
{
	++s2Map[c];
}
if (s1Map.size() != s2Map.size()) return false;
for (auto x : s1Map)
{
	if (s2Map[x.first] != x.second) 
		return false;
}

但是!別人直接sort,然後對比就完事。image

菜鳥落淚


剪枝完畢,再來跑下試試
image

image

那麼最後一招,記憶化遞迴
在每次計算出某時的isScramble(s1,s2)時,塞到容器裡,下次如果還遇到這倆s1,s2時,直接從容器裡取。
OK,通過。


class Solution {
	map<pair<string, string>, bool> checkmap;
public:
	bool isScramble(string s1, string s2) {
		if (checkmap.count(pair<string,string>(s1,s2)) > 0) return checkmap[pair<string,string>(s1,s2)];
		if (s1 == s2)
		{
			checkmap[pair<string, string>(s1, s2)] = true;
			return true;
		}
		
		string _s1 = s1, _s2 = s2;
		sort(_s1.begin(), _s1.end());
		sort(_s2.begin(), _s2.end());
		if (_s1!=_s2)
		{
			checkmap[pair<string, string>(s1, s2)] = false;
			return false;
		}
		int n = s1.size();
		for (int i = 1; i < n; ++i)
		{
			//這裡用的assign
			/*string temp_s1_front, temp_s1_back, temp_s2_front, temp_s2_back, temp_s2_front_re, temp_s2_back_re;

			temp_s1_front.assign(s1.begin(), s1.begin() + i);
			temp_s1_back.assign(s1.begin() + i, s1.end());

			temp_s2_front.assign(s2.begin(), s2.begin() + i);
			temp_s2_back.assign(s2.begin() + i, s2.end());

			temp_s2_front_re.assign(s2.end() - i, s2.end());
			temp_s2_back_re.assign(s2.begin(), s2.end() - i);

			if ((isScramble(temp_s1_front, temp_s2_front) && isScramble(temp_s1_back, temp_s2_back)) || (isScramble(temp_s1_front, temp_s2_front_re) && isScramble(temp_s1_back, temp_s2_back_re)))
			{
				checkmap[pair<string, string>(s1, s2)] = true;
				return true;
			}*/
			//這裡選擇用substr
			//拋開伺服器波動,substr佔優勢(不過上面用的是迭代器,這裡用的下標,應該不會有太大影響)
			if (isScramble(s1.substr(0, i), s2.substr(0, i)) && isScramble(s1.substr(i, n - i), s2.substr(i, n - i)) ||
				isScramble(s1.substr(0, i), s2.substr(n - i, i)) && isScramble(s1.substr(i, n - i), s2.substr(0, n - i)))
			{
				checkmap[pair<string, string>(s1, s2)] = true;
				return true;
			}
		}
		checkmap[pair<string, string>(s1, s2)] = false;
		return false;
	}
};

另外,容器這裡我原本用的unordered_map,底層是hash表,但是發現unordered_map不能以pair作為Key,之前也遇到過了,這次又遇到,就查了一下,發現是因為unordered_map沒有寫以pair作為key的hash函式。

//我來嘗試寫一個
struct pairhash
{
	template<typename T1,typename T2>
	size_t operater()(const pair<T1,T2>& _p)
	{
		//第一個括號是生成物件,第二個帶參的括號是呼叫hash<T1>::operater()
		auto _first = hash<T1>()(_p.first);
		auto _second = hash<T2>()(_p.second);
		return _first^_second;
	}
}
//經測試,OK

相關文章