LeetCode87 Scramble String
問題來源LeetCode87
問題描述
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.
問題分析
這道題說了一種爬行字符串,就是說假如把一個字符串當做一個二叉樹的根,然後它的非空子字符串是它的子節點,然後交換某個子字符串的兩個子節點,重新爬行回去形成一個新的字符串,這個新字符串和原來的字符串互爲爬行字符串。
這裏比較關鍵的就是節點內的字符串交換。看到題目給的樹狀模型,可以考慮採用分割法進行解決。
把s1和s2從i部分切割開來
- 判斷s1的左邊與s2的左邊和s1的右邊與s2的右邊是否互爲爬行字符串,
- 或者s1的左邊與s2的右邊和s1的右邊與s2的左邊是否爲爬行字符串。
代碼如下
public boolean isScramble(String s1, String s2) {
if(s1.length()!=s2.length())
//如果長度不相同,直接返回false
return false;
if(s1.equals(s2))
//如果相等,直接返回true
return true;
//字符串轉化爲字符數組
char[] c1 = s1.toCharArray();
char[] c2 = s2.toCharArray();
//進行字典排序
Arrays.sort(c1);
Arrays.sort(c2);
//如果字典排序後不想等,返回false
if(!Arrays.equals(c1,c2))
return false;
for (int i = 1; i < s1.length(); i++) {
if(isScramble(s1.substring(0,i),s2.substring(0,i))
&& isScramble(s1.substring(i),s2.substring(i)))
return true;
if(isScramble(s1.substring(0,i),s2.substring(s2.length()-i))
&& isScramble(s1.substring(i),s2.substring(0,s2.length()-i)))
return true;
}
return false;
}
問題分析2
以上算法的時間複雜度已經超過了多項式的複雜度,一般對於遞歸算法,都可以使用動態規劃的方法來解決,那麼這道題應該如何解決呢。
借鑑之前字符串的題目採用二維數組 dp[][] 來保存顯然是不夠的,在上面的算法中看到每次遞歸都需要對字符串掃描一遍,那麼可以考慮使用三維數組 dp[][][] 來保存數據。(靈感來源於http://blog.csdn.net/linhuanmars/article/details/24506703,感謝大佬)
其中 dp[j][k][i] 代表的是i,j,k代表的是字符串s1的從j位置開始,字符串s2從k開始,長度爲i的部分是否互爲爬行字符串。
那麼動態規劃的迭代公式應該是
dp[j][k][i]|=dp[j][k][l]&&dp[j+l][k+l][i-l] ||dp[j][k+i-l][l]&&dp[j+l][k][i-l];
這裏採用 “|=” 是因爲對於所有i-1種劈法的結果求或運算,只要有一種滿足即可。
動態規劃的時間複雜度爲 O(n^4) 空間複雜度爲 O(n^3), 雖然比之前的很多題目都要耗時間,但是也算是不錯的了。
代碼如下
public boolean isScramble2(String s1,String s2){
if(s1.length()!=s2.length())
return false;
if(s1.equals(s2))
return true;
int length = s1.length();
boolean[][][] dp = new boolean[length][length][length+1];
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
dp[i][j][1]= s1.charAt(i)==s2.charAt(j);
}
}
for(int i=2;i<=length;i++){
for (int j = 0; j < length-i+1; j++) {
for (int k = 0; k < length-i+1; k++) {
for (int l = 1; l < i; l++) {
dp[j][k][i]|=dp[j][k][l]&&dp[j+l][k+l][i-l] ||dp[j][k+i-l][l]&&dp[j+l][k][i-l];
}
}
}
}
return dp[0][0][length];
}
LeetCode學習筆記持續更新
GitHub地址 https://github.com/yanqinghe/leetcode
CSDN博客地址 http://blog.csdn.net/yanqinghe123/article/category/7176678