LeetCode87 Scramble String

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.

問題分析

這道題說了一種爬行字符串,就是說假如把一個字符串當做一個二叉樹的根,然後它的非空子字符串是它的子節點,然後交換某個子字符串的兩個子節點,重新爬行回去形成一個新的字符串,這個新字符串和原來的字符串互爲爬行字符串。

這裏比較關鍵的就是節點內的字符串交換。看到題目給的樹狀模型,可以考慮採用分割法進行解決。

s1s2i部分切割開來

  1. 判斷s1的左邊與s2的左邊和s1的右邊與s2的右邊是否互爲爬行字符串,
  2. 或者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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章