leetcode 87 扰乱字符串
题目意思就是说判断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