Scramble String

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.

据说这是区间型dp的终极版本,其实难点在于如何定义状态,状态定义好了不难.首先这也是双序列问题,状态中肯定包括第一个序列的第i个,第二个序列的第j个,但是仅仅用这个不够,我们需要加上第三维即长度,最终的转换状态是dp[x][y][k],即第一个序列的x位开始,长度为k的序列和第二个序列的y位开始,长度为k的序列,是否是scramble string.

状态转换,显然像rgtae一样,我们需要找一个分割的位置,使得s2[y:y+k-1]按位置分割的这两段分别对应s1[x:x+k-1]中的两段:一共有两种对应关系,一种左边对应左边,右边对应右边,另外一种是左边对应右边,右边对应左边.如tae对应eat,或者eat对应eat.请看示意图:

初始化,可以先检查每个字符是否对应,即dp[x][y][1].

最终结果是dp[0][0][n].代码如下:

class Solution(object):
    def isScramble(self, s1, s2):
        """
        :type s1: str
        :type s2: str
        :rtype: bool
        """
        if len(s1) != len(s2):
            return False
        n = len(s1)
        dp = [[[False]*(n+1) for i in xrange(n)] for j in xrange(n)]
        for x in xrange(n):
            for y in xrange(n):
                if s1[x] == s2[y]:
                    dp[x][y][1] = True
        for k in xrange(2, n+1):
            for x in xrange(0, n-k+1):
                for y in xrange(0, n-k+1):
                    for i in xrange(1, k):
                        if (dp[x][y][i] and dp[x+i][y+i][k-i])  or (dp[x][y+k-i][i] and dp[x+i][y][k-i]):
                            dp[x][y][k] = True
                            break
        return dp[0][0][n]
                           

空间复杂度为O(n^3),时间复杂度为O(n^4).

这题如果不采用动态规划而是暴力递归解法.则需要枚举s1开始结束位置,s2开始结束位置,枚举s1和s2的切分位,所以复杂度为O(n^6).但是可以采取剪枝等优化方法.

有两种加速策略,一种是剪枝,提前返回;一种是加缓存,缓存中间结果,即 memorization(翻译为记忆化搜索)。具体参见leetcode-cpp题解.

posted on 2016-06-25 16:25  Sheryl Wang  阅读(192)  评论(0编辑  收藏  举报

导航