題目意思就是說判斷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;
}
};
邏輯沒問題,答案也沒錯。但是嚴重超時。
剪枝!
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,然後對比就完事。
菜鳥落淚
剪枝完畢,再來跑下試試
那麼最後一招,記憶化遞迴
在每次計算出某時的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