【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,将字符串转为整数. 在找到第一个非空字符之前,需要移除掉字符串中的空格字符.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即 ...
随机推荐
- ORACLE截取字符串
每行显示固定字符串,截取字符串 方法一:在循环里面输出 DECLARE l_char VARCHAR2 (3000 ) := 'ORACLEEB电子商务套件SSYSTEMg ...
- 隐藏ion-nav-back-button的文字
<ion-nav-bar class="bar-positive"> <ion-nav-back-button> </ion-nav-back-but ...
- CentOS 安装rz和sz
在虚机装了CentOS6.4版本,结果ssh上去后发现rz命令不能用.那只有自力更生了,其实很简单,如下: yum install lrzsz 如果本地没有包,需要编译安装了: cd /tmp wge ...
- asp.net core mvc视频A:笔记1.基本概念介绍
此笔记来自视频教程 MVC本身与三层架构没有联系 使用VS2017新建一个默认的asp.net core mvc网站,认识结构及文件用途.
- mysql负载飙高原因分析
某些进程/服务消耗更多CPU资源(服务响应更多请求或存在某些应用瓶颈):发生比较严重的swap(可用物理内存不足):发生比较严重的中断(因为SSD或网络的原因发生中断):磁盘I/O比较慢(会导致CPU ...
- php单元测试入门教程phpunit详解
本文档提供了一些phpunit官方教程没有提到的信息,帮助初学者快速了解php单元测试,在phpunit官网提供了详细的中文教程,可选多种格式下载 phpunit官网地址:https://phpuni ...
- 【HDU 5316】Magician(线段树)
一開始度错题了,题意是求一段和最大的[子序列],要求相邻两个元素的位置必须互为奇偶. 这样我们能够使用线段树维护4个值: 一段区间内开头结尾元素为: 奇奇 奇偶 偶奇 偶偶 的最大值 之后在pushu ...
- Atitit.跨语言系统服务管理器api兼容设计
Atitit.跨语言系统服务管理器api兼容设计 1. Common api,兼容sc ,service control??1 1.1. 服务创建,use sc1 1.2. 服务delete ,use ...
- Atitit.获取approot api 应用根路径 java c#.net php asp
Atitit.获取approot api 应用根路径 java c#.net php asp 1. 如果根路径返回empty,否则返回/app,兼容getContextPath() <scrip ...
- 不同手机根据坐标计算控件、图片的像素,px 与 dp, sp换算公式?
参考该帖子:http://www.cnblogs.com/bluestorm/p/3640786.html PPI = Pixels per inch,每英寸上的像素数,即 "像素密度&qu ...