扰乱字符串
题目描述:
给定一个字符串 s1,我们可以把它递归地分割成两个非空子字符串,从而将其表示为二叉树。下图是字符串 s1 = "great" 的一种可能的表示形式。great/ \gr eat/ \ / \g r e at/ \a t在扰乱这个字符串的过程中,我们可以挑选任何一个非叶节点,然后交换它的两个子节点。例如,如果我们挑选非叶节点 "gr" ,交换它的两个子节点,将会产生扰乱字符串 "rgeat" 。rgeat/ \rg eat/ \ / \r g e at/ \a t我们将 "rgeat” 称作 "great" 的一个扰乱字符串。同样地,如果我们继续交换节点 "eat" 和 "at" 的子节点,将会产生另一个新的扰乱字符串 "rgtae" 。rgtae/ \rg tae/ \ / \r g ta e/ \t a我们将 "rgtae” 称作 "great" 的一个扰乱字符串。给出两个长度相等的字符串 s1 和 s2,判断 s2 是否是 s1 的扰乱字符串。
class Solution {
public boolean isScramble(String s1, String s2) {
// 初始化
int len = s1.length();
boolean[][][] dp = new boolean[len+1][len][len];
for(int i = 0 ; i<len ; i++){ // 初始化"平面"
for(int j = 0 ; j<len ; j++){
if(s1.charAt(i) == s2.charAt(j)) dp[1][i][j] = true;
}
}
// 动态规划过程
for(int t = 2 ; t<=len ; t++){ // 遍历划分长度
for(int i = 0 ; i+t <= len ; i++){ // 遍历s1开始位置
for(int j = 0 ; j+t <= len ; j++){ // 遍历s2开始位置
for(int k = 1 ; k<t ; k++){ // 遍历划分方式
if(dp[k][i][j] && dp[t-k][i+k][j+k] // 第一种方式:不变划分
|| dp[k][i][j+t-k] && dp[t-k][i+k][j]){ // 第二种方式:交换划分
dp[t][i][j] = true;
break;
}
}
}
}
}
return dp[len][0][0];
}
}
该题解决有一定的难度,第一感觉不一定能想到这是区间动态规划问题。
对于解决该题,第一步需要明确存储数组dp[len][i][j]的定义:s1字符串从下标位置i开始的长度为len的字符串区间可以是否能变换到s2字符串从下标j开始的长度为len的字符区间
第二步则需要确定转移方程:
dp[t][i][j] = {dp[k][i][j](s1和s2前半截) && dp[t-k][i+k][j+k](s1和s2后半截) || dp[k][i][j+t-k](s1前半截和s2后半截) && dp[t-k][i+k][j](s1后半截和s2前半截)}对于以上的转移方程,这里给出解释:结合题意容易知道,这里只有两种情况,一种情况就是位置未发生调换就由s1变换到s2,另一种情况就是s1的前一部分变换到s2的后一部分位置,即调换了位置。如下图:
详细请看代码,有疑问欢迎留言。