Scramble String
来源:互联网 发布:java面试专业技能 编辑:程序博客网 时间:2024/05/22 22:10
https://oj.leetcode.com/problems/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.
public boolean isScramble(String s1, String s2)
这一题,和find median in two sorted array在我眼里是并列oj所有题目top 2难度的,这一题同时也是oj里面目前为止最难的,也是唯一的三维DP。
当然,跟以往一样,在给出dp解之前,我会给出一个可以AC的暴力递归截枝解。
这题暴力的做法其实不是很难。主要是在每一个递归层级的当前s1和s2 中,遍历一个分界点,也就是取一个i, 0 < i < s.length(),然后进行两次分组。
第一个分组是s1[0.. i - 1], s2[0.. i - 1]的搭配以及s1[i...s1.length() - 1], s2[i...s2.length() - 1]的搭配
第二个分组是s1[0...i - 1] s2[s2.length() - i , s2.length() - 1]的搭配以及s1[i.. s1.length() - 1]. s2[0... s2.length() - i - 1]的搭配。
以上两组共四个搭配分别往下递归。也就是每组两次,分别两组递归。当第一组可以返回true的时候就不需要进行第二组了,接下来的分割也是不需要了。因为找到一组合理的就可以了。
当所有分割分组的递归都没办法返回true的时候就只能返回false了。这个递归的base case是搭配的子字符串相等即返回true。
至于截枝的方法,只能在每次开展循环分割并递归的之前,判断s1和s2的字符数目是否相等。如果不相等直接返回false就可以了。
下面给出暴力的代码:
public boolean isScramble(String s1, String s2) { if(s1.length() != s2.length()) return false; return helper(s1, s2); } public boolean helper(String s1, String s2){ if(s1.equals(s2)) return true; int[] counter = new int[26]; for(int i = 0; i < s1.length(); i++){ counter[s1.charAt(i) - 'a']++; } for(int i = 0; i < s2.length(); i++){ counter[s2.charAt(i) - 'a']--; } for(int i : counter){ if(i != 0) return false; } for(int i = 1; i < s1.length(); i++){ String s11 = s1.substring(0, i), s12 = s1.substring(i), s21 = s2.substring(0, i), s22 = s2.substring(i); if(helper(s11, s21) && helper(s12, s22)) return true; String s23 = s2.substring(0, s1.length() - i), s24 = s2.substring(s1.length() - i); if(helper(s11, s24) && helper(s12, s23)) return true; } return false; }至于DP解,其实也是基于上述暴力解的一个方案
首先给出f(i, j, k)的定义,就是当s1以第i个字母开始,s2以第j个字母开始,k长度的子串是否是scrambled的。
那么这个dp的base case是f(i, j , 1) = s1[i] == s2[j].
那么在k >= 2的环境里f(i,j,k) = f(i,j, m) && f(i + m,j + m, k - m) || f(i, j + k - m, m) && f(i + m, j, k - m) ( 1 <= m < k)
其实上述的推导式里面在我们的暴力递归式就已经可以看到了。具体就不详述了,
public boolean isScramble(String s1, String s2) { if(s1.length() != s2.length()) return false; boolean[][][] dp = new boolean[s1.length()][s1.length()][s1.length() + 1]; for(int i = 0; i < s1.length(); i++){ for(int j = 0; j < s2.length(); j++){ dp[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; i++){ for(int j = 0; j <= s2.length() - len; j++){ for(int k = 1; k < len; k++){ dp[i][j][len] |= (dp[i][j][k] && dp[i + k][j + k][len - k]) // 相当于brute force的正向比较 || (dp[i][j + len - k][k] && dp[i + k][j][len - k]);// brute force的反向比较 } } } } return dp[0][0][s1.length()]; }
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- Scramble String
- scramble string
- Scramble String
- Scramble String
- Scale和Resolution的含义及转换算法
- [UVALive4864] Bit Counting && 数位DP
- 【白话经典算法系列之十七】 数组中只出现一次的数
- Java发HTTP POST请求(内容为xml格式)
- Java基本类型和引用类型
- Scramble String
- 39级台阶
- 补零与离散傅里叶变换的分辨率
- Java 引用传递
- U-Boot移植--支持串口Xmodem协议
- [UVALive5058] Counting BST && 计数DP + BST
- 01背包
- 1036. Boys vs Girls (25)
- 定位不到元素的原因