给定一个字符串 s1,我们可以把它递归地分割成两个非空子字符串,从而将其表示为二叉树。
下图是字符串 s1 = "great" 的一种可能的表示形式。
great
/ \
gr eat
/ \ / \
g r e at
/ \
a t
在扰乱这个字符串的过程中,我们可以挑选任何一个非叶节点,然后交换它的两个子节点。
例如,如果我们挑选非叶节点 "gr" ,交换它的两个子节点,将会产生扰乱字符串 "rgeat" 。
rgeat
/ \
rg eat
/ \ / \
r g e at
/ \
a t
我们将 "rgeat” 称作 "great" 的一个扰乱字符串。
同样地,如果我们继续交换节点 "eat" 和 "at" 的子节点,将会产生另一个新的扰乱字符串 "rgtae" 。
rgtae
/ \
rg tae
/ \ / \
r g ta e
/ \
t a
我们将 "rgtae” 称作 "great" 的一个扰乱字符串。
给出两个长度相等的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。
示例 1:
输入: s1 = "great", s2 = "rgeat" 输出: true
示例 2:
输入: s1 = "abcde", s2 = "caebd" 输出: false
题解
1 主要是使用递归,但是如果单纯使用递归则可能出现tle,这里利用异或来排除肯定不可能是搅乱字符串的字符串
/**
* @param {string} s1
* @param {string} s2
* @return {boolean}
*/
function isScramble(s1, s2) {
if (s1.length != s2.length) return false;
if (s1 == s2) return true;
if (s1.length == 0 || s2.length == 0) return true;
let s1Arr = new Array(26).fill(0);
let s2Arr = new Array(26).fill(0);
// 统计s1中出现a-z的个数,统计s2中a-z出现的个数,然后对比,假设如果出现a的次数不相等,则可以直接返回false
for (let i = 0; i < s1.length; i++) {
s1Arr[s1.charCodeAt(i) - 97]++;
s2Arr[s2.charCodeAt(i) - 97]++;
}
for (let i = 0; i < 26; i++) {
if (s1Arr[i] !== s2Arr[i]) {
return false;
}
}
// 递归寻找是否是是相对搅乱的字符串
const len = s1.length;
// 前面是否相同,通过异或来判断,比如1^1 = 0
let sameBegin = 0;
let sameLast = 0;
for (let i = 0, j = len - 1; i < len; i++, j--) {
// 如果前面是可能是搅乱的, 比如[1,2,3] 和 [3,2,1] , 1^3^2^2^3^1 = 0
sameBegin ^= s1.charCodeAt(i) ^ s2.charCodeAt(i);
// 如果后面可能是搅乱的
sameLast ^= s1.charCodeAt(i) ^ s2.charCodeAt(j);
if (sameBegin === 0 && i < len - 1) {
if (
isScramble(s1.substring(0, i + 1), s2.substring(0, i + 1)) &&
isScramble(s1.substring(i + 1, len), s2.substring(i + 1, len))
) {
return true;
}
}
if (sameLast == 0 && i < len - 1) {
if (
isScramble(s1.substring(0, i + 1), s2.substring(len - i - 1)) &&
isScramble(s1.substring(i + 1), s2.substring(0, len - i - 1))
) {
return true;
}
}
}
return false;
}
export default isScramble;