码迷,mamicode.com
首页 > 其他好文 > 详细

[LeetCode#87] Scramble String

时间:2015-09-20 23:51:57      阅读:257      评论:0      收藏:0      [点我收藏+]

标签:

Problem:

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.

Analysis:

The instant idea of this problem is easy.
Apparently, you use DFS method to constuct the scrmble string of s1. Once you detect out a scramble string s1 equal to s2. Then you can say s2 is the scramble string of s1.

Inefficient solution 1:

public class Solution {
    public boolean isScramble(String s1, String s2) {
        if (s1 == null && s2 == null)
            return true;
        if (s1 == null || s2 == null)
            return false;
        if (s1.length() != s2.length())
            return false;
        for (int i = 0; i < s1.length(); i++) {
            List<String> left = swapHelper(s1.substring(0, i));
            List<String> right = swapHelper(s1.substring(i));
            if (combine(left, right, s2))
                return true;
        }
        return false;
    }
    
    
    private ArrayList<String> swapHelper(String s) {
        ArrayList<String> ret = new ArrayList<String> ();
        if (s.length() == 0) {
            ret.add("");
            return ret;
        }
        if (s.length() == 1) {
            ret.add(s);
            return ret;
        }
        for (int i = 0; i < s.length(); i++) {
            List<String> left = swapHelper(s.substring(0, i));
            List<String> right = swapHelper(s.substring(i));
            for (String l_part : left) {
                for (String r_part: right) {
                    ret.add(l_part + r_part);
                    ret.add(r_part + l_part);
                }
            }
        }
        return ret;
    }
    
    
    private boolean combine(List<String> left, List<String> right, String s2) {
        for (String l_part : left) {
            for (String r_part: right) {
                if (s2.equals(l_part+r_part) || s2.equals(r_part + l_part))
                    return true;
            }
        }
        return false;
    } 
}

Improved Analysis:

Even though above solution is right, it faces the problem of TLE. To search for the scramble string collections, we have too much uncessary search, which is quite expensive in this problem. 


Actually, we could use a more easy way to solve this problem. And this is a typical recursive problem. Since according to the definition of the problem. The constructing process of possible scramble string is a recursive process. 
The process: 
    great
   /      gr    eat
 / \    /  g   r  e   at
           /           a   t


=>

    rgeat
   /      rg    eat
 / \    /  r   g  e   at
           /           a   t


Let us randomly choose an index "i" on the string s1. 
Then we treat s[0, i] as left-sub tree and s[i+1, len-1] as right sub-tree.
Then, acoording to the process of constructing a Scramle String, we could swap them (or not swap them) to get a scramble string. 
And furtherly, we could keep on doing this transform over let-sub tree and right-sub tree, which would also produce the scramble strings of the orginal string s.


This is recursive is so clearly!!! But how could way make it more efficiently during the process of searching. 
=>If you solely do scramble string transformation over a string, you still need to do a lot of uncessary search. 
A way to fix this problem is to include the information from s2. And then use the s2‘s information to cut-off uncessary search branches. 

Key point:
Iff s1 and s2 are scramble strings with each other, 
There must be a point(swap point) one s1 and s2, to index "i" to cut-off the string into two separate substrings(sub-tree).
s1[0, i) s1[i, len-1]
s2[0, i) s2[i, len-1]
//when the left-sub tree and right-sub tree of s1 not need to swap position

or 

s1[0, i) s1[i, len-1]
s2[len-i, len-1] s2[0, len-i)
//when then left-sub tree and right-sub tree of s1 need to swap position 

for (int i = 1; i < len; i++) { 
    String s11 = s1.substring(0, i);
    String s12 = s1.substring(i, len);
    String s21 = s2.substring(0, i);
    String s22 = s2.substring(i, len);
    if (isScramble(s11, s21) && isScramble(s12, s22))
        return true;
    s11 = s1.substring(0, len - i);
    s12 = s1.substring(len - i, len);
    if (isScramble(s11, s22) && isScramble(s12, s21))
        return true;
}
------------------------------------------------------------------------------------
for (int i = 1; i < len; i++)
Note: we must start from i = 1, otherwise the same string would keep on entering into isScramble(), and result in infinite loop.

Apparently, the above search cost is very high!!! But we could find a method to do properly cut-off during the process.
1. if (s1.length() != s2.length()), it is impossible for s1 and s2 are scramble string with each other. (this is the test for s1 and s2 just enter the isScramble string) In the afterward calls, this situation would not happen, since we control the s1 and s2‘s length must equal with each other through:
String s11 = s1.substring(0, i);
String s12 = s1.substring(i, len);
String s21 = s2.substring(0, i);
String s22 = s2.substring(i, len);
or 
s11 = s1.substring(0, len - i);
s12 = s1.substring(len - i, len);

Note: this kind of base case is very common in elegant recursive solution. it also tackle the case of inital illegal call.


2. most important base case: if (s1.equals(s2)). 
It means s1 must be the scramble string of s2. The worst case is we reach this base case as two characters.
s1 = "a"
s2 = "a"


3. the count of each characters should be equal. //this cut-off policy is super useful, it could greatly improve efficiency.
for (int i = 0; i < len; i++) {
    count[s1.charAt(i)-‘a‘]++;
    count[s2.charAt(i)-‘a‘]--;
}

Upgraded Solution:

public class Solution {
    public boolean isScramble(String s1, String s2) {
        if (s1.length() != s2.length())
            return false;
        if (s1.equals(s2))
            return true;
        int[] count = new int[26];
        int len = s1.length();
        for (int i = 0; i < len; i++) {
            count[s1.charAt(i)-‘a‘]++;
            count[s2.charAt(i)-‘a‘]--;
        }
        for (int i = 0; i < 26; i++) {
            if (count[i] != 0)
                return false;
        }
        for (int i = 1; i < len; i++) {
            String s11 = s1.substring(0, i);
            String s12 = s1.substring(i, len);
            String s21 = s2.substring(0, i);
            String s22 = s2.substring(i, len);
            if (isScramble(s11, s21) && isScramble(s12, s22))
                return true;
            s11 = s1.substring(0, len - i);
            s12 = s1.substring(len - i, len);
            if (isScramble(s11, s22) && isScramble(s12, s21))
                return true;
        }
        return false;
    }
}

 

[LeetCode#87] Scramble String

标签:

原文地址:http://www.cnblogs.com/airwindow/p/4824547.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!