Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.
Below is one possible representation of s1 = "great":
great
/ \
gr eat
/ \ / \
g r e at
/ \
a t
To scramble the string, we may choose any non-leaf node and swap its two children.
For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat".
rgeat
/ \
rg eat
/ \ / \
r g e at
/ \
a t
We say that "rgeat" is a scrambled string of "great".
Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".
rgtae
/ \
rg tae
/ \ / \
r g ta e
/ \
t a
We say that "rgtae" is a scrambled string of "great".
Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1.
class Solution {
public:
bool isScramble(string s1, string s2) {
if(s1.empty() || s2.empty() || s1.size()!=s2.size()) return false;
if(s1 == s2) return true;
string c1(s1);
sort(c1.begin(),c1.end());
string c2(s2);
sort(c2.begin(),c2.end());
if(c1 != c2) return false;
for(int i = 1;i < s1.size(); i++)
{
if(isScramble(s1.substr(0,i), s2.substr(0,i)) && isScramble(s1.substr(i), s2.substr(i)))
return true;
if(isScramble(s1.substr(0,i), s2.substr(s2.length()-i)) && isScramble(s1.substr(i), s2.substr(0, s2.length()-i)))
return true;
}
return false;
}
};
原题链接: http://oj.leetcode.com/problems/scramble-string/
这道题看起来是比较复杂的,如果用brute force,每次做切割,然后递归求解,是一个非多项式的复杂度,一般来说这不是面试官想要的答案。
这其实是一道三维动态规划的题目,我们提出维护量res[i][j][n],其中i是s1的起始字符,j是s2的起始字符,而n是当前的字符串长度,res[i][j][len]表示的是以i和j分别为s1和s2起点的长度为len的字符串是不是互为scramble。
有
了维护量我们接下来看看递推式,也就是怎么根据历史信息来得到res[i][j][len]。判断这个是不是满足,其实我们首先是把当前
s1[i...i+len-1]字符串劈一刀分成两部分,然后分两种情况:第一种是左边和s2[j...j+len-1]左边部分是不是
scramble,以及右边和s2[j...j+len-1]右边部分是不是scramble;第二种情况是左边和s2[j...j+len-1]右边部
分是不是scramble,以及右边和s2[j...j+len-1]左边部分是不是scramble。如果以上两种情况有一种成立,说明
s1[i...i+len-1]和s2[j...j+len-1]是scramble的。而对于判断这些左右部分是不是scramble我们是有历史信息
的,因为长度小于n的所有情况我们都在前面求解过了(也就是长度是最外层循环)。
上面说的是劈一刀的情况,对于s1[i...i+len-1]我们有len-1种劈法,在这些劈法中只要有一种成立,那么两个串就是scramble的。
总
结起来递推式是res[i][j][len] = || (res[i][j][k]&&res[i+k][j+k][len-k]
|| res[i][j+len-k][k]&&res[i+k][j][len-k])
对于所有1<=k<len,也就是对于所有len-1种劈法的结果求或运算。因为信息都是计算过的,对于每种劈法只需要常量操作即可完成,因
此求解递推式是需要O(len)(因为len-1种劈法)。
如此总时间复杂度因为是三维动态规划,需要三层循环,加上每一步需要线行时间求解递推式,所以是O(n^4)。虽然已经比较高了,但是至少不是指数量级的,动态规划还是有很大有事的,空间复杂度是O(n^3)。代码如下:
- public boolean isScramble(String s1, String s2) {
- if(s1==null || s2==null || s1.length()!=s2.length())
- return false;
- if(s1.length()==0)
- return true;
- boolean[][][] res = new boolean[s1.length()][s2.length()][s1.length()+1];
- for(int i=0;i<s1.length();i++)
- {
- for(int j=0;j<s2.length();j++)
- {
- res[i][j][1] = s1.charAt(i)==s2.charAt(j);
- }
- }
- for(int len=2;len<=s1.length();len++)
- {
- for(int i=0;i<s1.length()-len+1;i++)
- {
- for(int j=0;j<s2.length()-len+1;j++)
- {
- for(int k=1;k<len;k++)
- {
- res[i][j][len] |= res[i][j][k]&&res[i+k][j+k][len-k] || res[i][j+len-k][k]&&res[i+k][j][len-k];
- }
- }
- }
- }
- return res[0][0][s1.length()];
- }
个 人觉得这是LeetCode中最难的动态规划的题目了,要进行一次三维动态规划,对于维护量的含义也比较讲究。有朋友会讨论这个维护量是怎么提出来的,我 自己也没什么绝对的方法,还是熟能生巧,靠“感觉”,做的题目多了就自然来了,这个做高中数学题有点类似哈,辅助线是靠“灵感”的哈。面试中如果遇到就是 top难度的了,不过即使如此,只要思路清晰,还是可以记住的。如果没做过,个人觉得比较难当场想出来,不过算法大牛就另说了,这种题很经常出现在编程比 赛中,ACM高手还是不在话下的哈。
class Solution {
public:
bool isScramble(string s1, string s2) {
if(s1.empty() || s2.empty() || s1.size()!=s2.size())
return false;
if(s1.size()==0)
return true;
const int n = s1.size();
vector<vector<vector<bool> > > res(n,vector<vector<bool> >(n,vector<bool>(n+1)));
for(int i = 0;i<n;i++)
{
for(int j=0;j<n;j++)
{
res[i][j][1] = s1[i]==s2[j];
}
}
for(int len=2;len<=n;len++)
{
for(int i=0;i<n-len+1;i++)
{
for(int j=0;j<n-len+1;j++)
{
for(int k=1;k<len;k++)
{
res[i][j][len] = res[i][j][len] || res[i][j][k]&&res[i+k][j+k][len-k] || res[i][j+len-k][k]&&res[i+k][j][len-k];
}
}
}
}
return res[0][0][s1.size()];
}
};
class Solution {
public:
bool isScramble(string s1, string s2) {
const int n = s1.size();
if (n != s2.size()) return false;
if (n == 0) return true;
bool SC[n][n][n];
for (int i = 0; i < n; i++) { // len is 1
for (int j = 0; j < n; j++) {
SC[0][i][j] = s1[i] == s2[j];
}
}
for (int len = 2; len <= n; len++) {
for (int i = 0; i + len <= n; i++) {
for (int j = 0; j + len <= n; j++) {
SC[len-1][i][j] = false;
for (int l = 1; l < len && !SC[len-1][i][j]; l++) {
SC[len-1][i][j] = ((SC[l-1][i][j] && SC[len-l-1][i+l][j+l]) ||
(SC[l-1][i][j+len-l] && SC[len-l-1][i+l][j]));
}
}
}
}
return SC[n-1][0][0];
}
};
浙公网安备 33010602011771号