【leetcode刷题笔记】Scramble String
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。这样就有了我们的递归解法。
但是仅仅是这种递归一定会超时,必须进行优化,有一下几点可以优化的地方:
- s1和s2长度必须相等;
- 长度相等且都为0,返回true;
- s1和s2完全相同返回true;
- 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的更多相关文章
- 【leetcode刷题笔记】String to Integer (atoi)
Implement atoi to convert a string to an integer. Hint: Carefully consider all possible input cases. ...
- LeetCode刷题笔记和想法(C++)
主要用于记录在LeetCode刷题的过程中学习到的一些思想和自己的想法,希望通过leetcode提升自己的编程素养 :p 高效leetcode刷题小诀窍(这只是目前对我自己而言的小方法,之后会根据自己 ...
- LeetCode刷题笔记 - 12. 整数转罗马数字
学好算法很重要,然后要学好算法,大量的练习是必不可少的,LeetCode是我经常去的一个刷题网站,上面的题目非常详细,各个标签的题目都有,可以整体练习,本公众号后续会带大家做一做上面的算法题. 官方链 ...
- 18.9.10 LeetCode刷题笔记
本人算法还是比较菜的,因此大部分在刷基础题,高手勿喷 选择Python进行刷题,因为坑少,所以不太想用CPP: 1.买股票的最佳时期2 给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. ...
- Leetcode刷题笔记(双指针)
1.何为双指针 双指针主要用来遍历数组,两个指针指向不同的元素,从而协同完成任务.我们也可以类比这个概念,推广到多个数组的多个指针. 若两个指针指向同一数组,遍历方向相同且不会相交,可以称之为滑动窗口 ...
- 【leetcode刷题笔记】Interleaving String
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. For example,Given:s1 = ...
- 【leetcode刷题笔记】Reverse Words in a String
Given an input string, reverse the string word by word. For example,Given s = "the sky is blue& ...
- LeetCode刷题笔记(1-9)
LeetCode1-9 本文更多是作为一个习题笔记,没有太多讲解 1.两数之和 题目请点击链接 ↑ 最先想到暴力解法,直接双循环,但是这样复杂度为n平方 public int[] twoSum(int ...
- leetcode刷题笔记08 字符串转整数 (atoi)
题目描述 实现 atoi,将字符串转为整数. 在找到第一个非空字符之前,需要移除掉字符串中的空格字符.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即 ...
随机推荐
- Linux 下安装PHPunit
PHP 档案包 (PHAR) 要获取 PHPUnit,最简单的方法是下载 PHPUnit 的 PHP 档案包 (PHAR),它将 PHPUnit 所需要的所有必要组件(以及某些可选组件)捆绑在单个文 ...
- 基于vue + axios + lrz.js 微信端图片压缩上传
业务场景 微信端项目是基于Vux + Axios构建的,关于图片上传的业务场景有以下几点需求: 1.单张图片上传(如个人头像,实名认证等业务) 2.多张图片上传(如某类工单记录) 3.上传图片时期望能 ...
- eventfd
#include <sys/eventfd.h> int eventfd(unsigned int initval, int flags); eventfd() creates an &q ...
- lua学习笔记(二)
开始 程序块chunk 交互模式里输入的一行代 一个文件里的代码 也就是一连串的语句或命令 连续的lua语句之间不需要分隔符,但也可以使用分号,如果你愿意的话 ...
- iOS UIView添加阴影
_bottomView.layer.masksToBounds = NO; _bottomView.backgroundColor = [UIColor whiteColor]; _bottomVie ...
- iOS左滑手势失效
iOS7之后,苹果优化了一个小功能,就是对于UINavagationController堆栈里的UIViewController,只要轻轻在视图控制器的左边缘右滑一下,该视图控制器就会pop出栈(前提 ...
- LCD屏背光驱动调试心得---血的教训
开发板:明远智睿MY-IMX6-EK140 内核源码:linux-3.14.52 背光驱动IC:MP3202 调光原理:通过开发板的核心板PWM4引脚控制MP3202的EN脚,输出不同的占空比从而达到 ...
- cocos2dx 3.0rc怎样创建项目
转自官网的文档. How to Run cpp-tests on win32 In this article, I will show you how to run cpp-tests on your ...
- 蒙特卡洛方法计算圆周率的三种实现-MPI openmp pthread
蒙特卡洛方法实现计算圆周率的方法比较简单,其思想是假设我们向一个正方形的标靶上随机投掷飞镖,靶心在正中央,标靶的长和宽都是2 英尺.同时假设有一个圆与标靶内切.圆的半径是1英尺,面积是π平方英尺.如果 ...
- Linux文件查找命令
1. find find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件. find的使用格式如下: $ find <指定目录> <指定条件> <指定动作> ...