【leetcode】87. Scramble String 字符串樹形顛倒匹配

1. 題目

Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively.node

Below is one possible representation of s1 = "great":數組

great

/ \
gr eat
/ / \
g r e atcode

/ \
      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 atstring

/ \
      a   t

We say that "rgeat" is a scrambled string of "great".it

Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae".io

rgtae

/ \
rg tae
/ / \
r g ta eclass

/ \
  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.

題目的樣例是從中間開始分割作樹,題意實際上是說能夠從任意點開始切割做爲樹的根節點,其餘非葉子的子節點也是同樣能夠從任意點切割。

2. 思路

第一反應是直接採用遞歸計算,因爲有大量的重複計算,超時了。
改用迭代的方式,即用數組保存下所有的以及計算過的子串的匹配結果,直接提取結果,而不是重複計算。而後在迭代是從小往大的計算,每次均可以直接複用小的計算結果。

初始條件:當長度爲1時,若是字符串相等就符合,負責不符合。
迭代:當長度爲n時,將字符串表示的樹的根節點放在第2至n-1元素上。某一邊的子樹爲空時無心義的,由於就是自身。也就是長度爲n的字符串有n-1種狀況,分別是1,k,k的取值範圍是1到n-1。這n-1中狀況,只要有一種符合,就說明這個長度爲n的字符串符合。
每一種狀況,又有兩種方式,s1的前k個和s2的前k個比較,s1後n-k個和s2的後n-k比較。也能夠是s1的前k個和s2的後k個比較。兩種方式對應的就是這個子樹不交換和交換的場景。

3. 代碼

耗時:16ms

class Solution {
public:
    bool isScramble(string s1, string s2) {
        const int len = s1.length();
        if (len != s2.length()) { return false; }
        // 非字符集合相等的提早剪枝
        string sort_s1 = s1; sort(sort_s1.begin(), sort_s1.end());
        string sort_s2 = s2; sort(sort_s2.begin(), sort_s2.end());
        if (sort_s1 != sort_s2) { return false; }
        // dp[n][i][j]表示s1[i..i+n-1]和s2[j..j+n-1]兩個子串是不是scramble的
        // 迭代起點,對長度爲1的直接比較計算出結果; 其餘的初始化爲false
        bool*** dp = new bool**[len+1];
        for (int i = 0; i < len+1; i++) {
            dp[i] = new bool*[len];
            for (int j = 0; j < len; j++) {
                dp[i][j] = new bool[len];
                for (int k = 0; k < len; k++) {
                    if (i == 0 || i == 1 && s1[j] == s2[k]) {
                        dp[i][j][k] = true;
                    } else {
                        dp[i][j][k] = false;
                    }
                }
            }
        }
        // 依次計算長度更大的, 對於長度爲n的狀況,直接看全部的子串劃分狀況是否有一個知足的
        // 因爲n是遞增的,所以子串的都是已經計算過的
        for (int n = 2; n < len+1; n++) {
            for (int i = 0; i <= len - n; i++) {
                for (int j = 0; j <= len - n; j++) {
                    // 計算長度爲n的, s1[i, i+n-1]和s2[j, j+n-1]是不是scramble的
                    // 經過遍歷全部子串劃分是否存在有一個知足便可
                    for (int k = 1; k < n; k++) { // k是s1部分的左子串的長度
                        if (dp[k][i][j] && dp[n-k][i+k][j+k]
                                || dp[k][i][j+n-k] && dp[n-k][i+k][j]) {
                            dp[n][i][j] = true;
                            break;
                        }
                    }
                }
            }
        }
        return dp[len][0][0];
    }
};
相關文章
相關標籤/搜索