题目:biubiu
题意:给定两个字符串,要求就是只能对一个字符串进行分割,分割成两个子串,然后两个子串可以交换,例如s1分割成s11和s12两个子串,两个子串可以s11+s12也可以s12+s11,两个子串可以继续相同的操作,问,现在s1==s2是否为真。
对字符串的操作其实非常简单,就是切割然后判断是否交换,其子串也可以继续切割,就涉及到了递归。
官方题解代码:
#include<iostream>
#include<string>
#include<vector>
#include<cmath>
#include<map>
#include<unordered_map>
#include<stack>
#include<algorithm>
using namespace std;
/*
class Solution {
public:
bool judge(string s1, string s2,int x) {
if (s1.size() == 1) {
if (s1 == s2)
return true;
else
return false;
}
int y=x+1;
int i;
for ( i = 1; i < s2.size(); i++) {
if (y == s1.size())
break;
if (s1[y] == s2[i]) {
y++;
}
else {
break;
}
}
string s3, s4;
s3 = s1.substr(0, x);
s4 = s2.substr(i, x);
//cout << s3 << "****" << s4 << endl;
if (s3==s4||isScramble(s3, s4))
{
if (y == s1.size())
return true;
s3 = s1.substr(y);
s4 = s2.substr(y);
//cout << s3 << "##$##" << s4 << endl;
if (s3==s4||isScramble(s3, s4))
return true;
else
return false;
}
else {
return false;
}
}
bool isScramble(string s1, string s2) {
if (s1 == s2)
return true;
for (int i = 0; i < s1.size(); i++) {
if (s1[i] != s2[i]) {
s1 = s1.substr(i);
s2 = s2.substr(i);
break;
}
}
for (int i = 0; i < s1.size(); i++) {
if (s1[i] == s2[0]) {
//cout << "*" << i << endl;
if (judge(s1, s2, i))
return true;
}
}
for (int i = 0; i < s1.size(); i++) {
if (s2[i] == s1[0]) {
//cout << "*" << i << endl;
if (judge(s2, s1, i))
return true;
}
}
return false;
}
};*/
class Solution {
private:
// 记忆化搜索存储状态的数组
// -1 表示 false,1 表示 true,0 表示未计算
int memo[30][30][31];
string s1, s2;
public:
bool checkIfSimilar(int i1, int i2, int length) {
unordered_map<int, int> freq;
for (int i = i1; i < i1 + length; ++i) {
++freq[s1[i]];
}
for (int i = i2; i < i2 + length; ++i) {
--freq[s2[i]];
}
if (any_of(freq.begin(), freq.end(), [](const auto& entry) {return entry.second != 0; })) {
return false;
}
return true;
}
// 第一个字符串从 i1 开始,第二个字符串从 i2 开始,子串的长度为 length,是否和谐
bool dfs(int i1, int i2, int length) {
if (memo[i1][i2][length]) {
return memo[i1][i2][length] == 1;
}
// 判断两个子串是否相等
if (s1.substr(i1, length) == s2.substr(i2, length)) {
memo[i1][i2][length] = 1;
return true;
}
// 判断是否存在字符 c 在两个子串中出现的次数不同
if (!checkIfSimilar(i1, i2, length)) {
memo[i1][i2][length] = -1;
return false;
}
// 枚举分割位置
for (int i = 1; i < length; ++i) {
// 不交换的情况
if (dfs(i1, i2, i) && dfs(i1 + i, i2 + i, length - i)) {
memo[i1][i2][length] = 1;
return true;
}
// 交换的情况
if (dfs(i1, i2 + length - i, i) && dfs(i1 + i, i2, length - i)) {
memo[i1][i2][length] = 1;
return true;
}
}
memo[i1][i2][length] = -1;
return false;
}
bool isScramble(string s1, string s2) {
memset(memo, 0, sizeof(memo));
this->s1 = s1;
this->s2 = s2;
return dfs(0, 0, s1.size());
}
};
int main() {
string s1, s2;
while (cin >> s1 >> s2) {
Solution s;
cout << s.isScramble(s1, s2) << endl;
}
return 0;
}
官方代码很好理解,递归切割然后标记,动态规划。这个题对字符串的操作其实很简单,就是切割然后是否交换位置,但是在最开始的时候没有想到这种方法,并且其实在题目描述中,描述的很清楚,已经给了递归的思路,并且,两个字符串匹配就要求字符的顺序相同,字符的数量相同,因此递归的时候只需要对切割的长度进行遍历即可。
发现自己思维打不开,其实这道题看过题解之后很好理解,就是切割,如果其子串可以匹配,那么这个字符串也就可以匹配,它的状态来源于子串的结果,并且子串的状态只有两种,交换,不变,s1分割成s11、s12,s2分割成s21、s22,那么不变的情况就是s11和s21进行匹配(s12-s22),同样,交换就是s11和s22进行匹配,子串的匹配不就和s1和s2匹配的问题相同,要找到解决问题的实际状态。