leetcode 87 扰乱字符串

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
posted @ 2021-04-16 15:58  抚琴思伯牙  阅读(96)  评论(0编辑  收藏  举报