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.


题解:

第一个想法比较naive,想把s1和s2都按照字典序排列,然后看是否相同。提交后就发现下面的例子就能够证明这个算法的错误:

s1 = "abcd"
s2 = "bdac"

第二个想法是递归,如下图所示:

对于任意长度相等的s1和s2,都可以分解成上图两种情况,通过把s1和s2分成两部分,那么有两种情况s1和s2是scramble的,一种是两部分对应scramble;一种是两部分交换后scramble。这样就有了我们的递归解法。

但是仅仅是这种递归一定会超时,必须进行优化,有一下几点可以优化的地方:

  1. s1和s2长度必须相等;
  2. 长度相等且都为0,返回true;
  3. s1和s2完全相同返回true;
  4. s1和s2所包含的字母种类个数必须相同,即s1和s2按照字典序排序后必须相同;

通过以上的优化,递归方法就不会超时了。

代码如下:

 public class Solution {
public boolean isScramble(String s1, String s2) {
int m = s1.length();
int n = s2.length(); if(m != n)
return false; if(m == 0 || s1.equals(s2))
return true; char[] chars1 = s1.toCharArray();
char[] chars2 = s2.toCharArray();
Arrays.sort(chars1);
Arrays.sort(chars2);
for(int i = 0;i < m;i++)
if(chars1[i] != chars2[i])
return false; for(int i = 1;i < m;i++){
String s1left = s1.substring(0,i);
String s1right = s1.substring(i);
String s2left = s2.substring(0, i);
String s2right = s2.substring(i); if(isScramble(s1left, s2left) && isScramble(s1right, s2right))
return true;
s2left = s2.substring(0,n-i);
s2right = s2.substring(n-i);
if(isScramble(s1left, s2right) && isScramble(s1right, s2left))
return true;
} return false;
}
}

上述方法耗时428ms。

第三个想法,动态规划。

递归之所以会超时,是因为做了许多重复的工作。用动态规划bottom-up的方法把这些重复的工作记录在表dp[][][]中,就可以省去重复计算的时间。

我们用dp[sublen][i][j]表示s1[i,i+sublen]和s2[j,j+sublen]是否是scramble的。那么有如下递推式:

dp[sublen][i][j] = (dp[k][i][j]&&dp[sublen-k][i+k][j+k]) || (dp[k][i][j+sublen-k] && dp[sublen-k][i+k][j]);

它对应了下图两种情况:

另外 dp[1][i][j] = s1.charAt(i) == s2.charAt(j);

理解还算容易,写代码还是有难度的,一共四重循环:

第一重循环改变sublen的大小,从长度为2的子串一直增加至长度为n的串;

第二重循环改变i,即s1中游标的位置,从0~n-sublen;

第三重循环改变i,即s2中游标的位置,从0~n-sublen;

第四重循环枚举从i~i+sublen之间的每一中分割字符串的位置,考察这样分割是否能使得s1和s2对应的部分scramble得到,k从1~sublen-1;

代码如下:

 public class Solution {
public boolean isScramble(String s1, String s2) {
if(s1.length() != s2.length())
return false; if(s1.length() == 0 && s2.length() == 0)
return true; int n = s1.length();
boolean[][][] dp = new boolean[n+1][n+1][n+1];
for(int i = 0;i < n;i++){
for(int j = 0;j < n;j++)
dp[1][i][j] = s1.charAt(i) == s2.charAt(j);
} for(int sublen = 2;sublen <= n;sublen++){
for(int i = 0;i <= n-sublen;i++){
for(int j = 0;j <= n-sublen;j++){
dp[sublen][i][j]= false;
for(int k = 1;k<sublen && dp[sublen][i][j]== false ;k++)
dp[sublen][i][j] = (dp[k][i][j]&&dp[sublen-k][i+k][j+k]) || (dp[k][i][j+sublen-k] && dp[sublen-k][i+k][j]);
}
}
}
return dp[n][0][0];
}
}

【leetcode刷题笔记】Scramble String的更多相关文章

  1. 【leetcode刷题笔记】String to Integer (atoi)

    Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input cases. ...

  2. LeetCode刷题笔记和想法(C++)

    主要用于记录在LeetCode刷题的过程中学习到的一些思想和自己的想法,希望通过leetcode提升自己的编程素养 :p 高效leetcode刷题小诀窍(这只是目前对我自己而言的小方法,之后会根据自己 ...

  3. LeetCode刷题笔记 - 12. 整数转罗马数字

    学好算法很重要,然后要学好算法,大量的练习是必不可少的,LeetCode是我经常去的一个刷题网站,上面的题目非常详细,各个标签的题目都有,可以整体练习,本公众号后续会带大家做一做上面的算法题. 官方链 ...

  4. 18.9.10 LeetCode刷题笔记

    本人算法还是比较菜的,因此大部分在刷基础题,高手勿喷 选择Python进行刷题,因为坑少,所以不太想用CPP: 1.买股票的最佳时期2 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. ...

  5. Leetcode刷题笔记(双指针)

    1.何为双指针 双指针主要用来遍历数组,两个指针指向不同的元素,从而协同完成任务.我们也可以类比这个概念,推广到多个数组的多个指针. 若两个指针指向同一数组,遍历方向相同且不会相交,可以称之为滑动窗口 ...

  6. 【leetcode刷题笔记】Interleaving String

    Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given:s1 = ...

  7. 【leetcode刷题笔记】Reverse Words in a String

    Given an input string, reverse the string word by word. For example,Given s = "the sky is blue& ...

  8. LeetCode刷题笔记(1-9)

    LeetCode1-9 本文更多是作为一个习题笔记,没有太多讲解 1.两数之和 题目请点击链接 ↑ 最先想到暴力解法,直接双循环,但是这样复杂度为n平方 public int[] twoSum(int ...

  9. leetcode刷题笔记08 字符串转整数 (atoi)

    题目描述 实现 atoi,将字符串转为整数. 在找到第一个非空字符之前,需要移除掉字符串中的空格字符.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即 ...

随机推荐

  1. window.name实现跨域数据传输

    偶然间碰到个问题,通过JS给window.name赋值数组情况下,在firefox与chrome下会转换为字符串类型,在IE11下则显示正常.不说了,上图(firefox下): 代码: <scr ...

  2. ajax个人学习笔记

    1. function createXHR(){ if(typeof XMLHttpRequest != 'undefined'){ return new XMLHttpRequest(); }els ...

  3. Storm/Cassandra集成错误:NoSuchMethodError: concurrent.Futures.withFallback

    本文原文出处: http://blog.csdn.net/bluishglc/article/details/50443205 严禁不论什么形式的转载.否则将托付CSDN官方维护权益. 2015年的最 ...

  4. c++打印蛇形矩阵

    一个m*n的矩阵里按照下图形式填充,最后形成的矩阵即为蛇形矩阵,下图是m=4, n =5时的蛇形矩阵: 方法一:逐层循环 #include <iostream> using namespa ...

  5. Machine Learning——Unsupervised Learning(机器学习之非监督学习)

    前面,我们提到了监督学习,在机器学习中,与之对应的是非监督学习.无监督学习的问题是,在未加标签的数据中,试图找到隐藏的结构.因为提供给学习者的实例是未标记的,因此没有错误或报酬信号来评估潜在的解决方案 ...

  6. Atitit.rust语言特性 attilax 总结

    Atitit.rust语言特性 attilax 总结 1. 创建这个新语言的目的是为了解决一个顽疾:软件的演进速度大大低于硬件的演进,软件在语言级别上无法真正利用多核计算带来的性能提升.1 2. 不会 ...

  7. getOutputStream() has already been called for this response的解决方法

    1.问题描述:spring mvc中下载文件结束后,跳转到list页面,问题报上面的异常. 2.原因:写文件的时候response调用一次,在跳转的时候,spring调用ActionForward类中 ...

  8. MVC进阶学习--View和Controller之间的数据传递(一)

    1.使用ViewData ViewData 的是ControllerBase 的一个属性,是一个数据字典类型的,其实现代码如(这段代码来自asp.net MVC开源项目中源码)下: Code   1  ...

  9. linux recv函数返回值分析

    函数原型: ssize_t recv(int sockfd, void *buf, size_t len, int flags); 该函数第一个参数制定接收端套接字描述符; 第二个参数指明一个缓冲区, ...

  10. vmware workstation导出ovf

    ovf tool路径 /Applications/VMware Fusion.app/Contents/Library/VMware OVF Tool 上面红色的部分,需要右键点击应用程序中的VMwa ...