zoukankan      html  css  js  c++  java
  • LeetCode: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.                                                                                本文地址

    递归解法:s2[0...j]是否可以由s1[0...i]转换 isScramble(s2[0...j], s1[0...i]),可以分解成 i 个子问题(i 其实等于j,因为两个字符串长度不一样,肯定不能互相转换):

    ( isScramble(s2[0...k], s1[0...k]) &&  isScramble(s2[k+1...j], s1[k+1...i]) ) || ( isScramble(s2[0...k], s1[i-k...i]) &&  isScramble(s2[k+1...j], s1[0...i-k-1]) ),(k = 0,1,2 ... i-1,k相当于字符串的分割点)

    只要一个子问题返回ture,那么就表示两个字符串可以转换。代码如下:

     1 class Solution {
     2 public:
     3     bool isScramble(string s1, string s2) {
     4         return isScrambleRecur(s1,s2);
     5     }
     6     bool isScrambleRecur(string &s1, string &s2)
     7     {
     8         string s1cop = s1, s2cop = s2;
     9         sort(s1cop.begin(), s1cop.end());
    10         sort(s2cop.begin(), s2cop.end());
    11         if(s1cop != s2cop)return false;//两个字符串所含字母不同
    12         if(s1 == s2)return true;
    13         
    14         int len = s1.size();
    15         for(int i = 1; i < len; i++)//分割位置
    16         {
    17             string s1left = s1.substr(0, i);
    18             string s1right = s1.substr(i);
    19             string s2left = s2.substr(0, i);
    20             string s2right = s2.substr(i);
    21             if(isScrambleRecur(s1left, s2left) && isScrambleRecur(s1right, s2right))
    22                 return true;
    23             s2left = s2.substr(0, len-i);
    24             s2right = s2.substr(len-i);
    25             if(isScrambleRecur(s1left, s2right) && isScrambleRecur(s1right, s2left))
    26                 return true;
    27         }
    28         return false;
    29     }
    30 };

    动态规划解法

    递归解法有很多重复子问题,比如s2 = rgeat, s1 = great 当我们选择分割点为0时,要解决子问题 isScramble(reat, geat),再对该子问题选择分割点0时,要解决子问题 isScramble(eat,eat);而当我们第一步选择1作为分割点时,也要解决子问题 isScramble(eat,eat)。相同的子问题isScramble(eat,eat)就要解决2次。

    动态规划用数组来保存子问题,设dp[k][i][j]表示s2从j开始长度为k的子串是否可以由s1从i开始长度为k的子串转换而成,那么动态规划方程如下

    1.    初始条件:dp[1][i][j] = (s1[i] == s2[j] ? true : false)
    2.    dp[k][i][j] = ( dp[divlen][i][j] && dp[k-divlen][i+divlen][j+divlen] )  ||  ( dp[divlen][i][j+k-divlen] && dp[k-divlen][i+divlen][j] ) (divlen = 1,2,3...k-1, 它表示子串分割点到子串起始端的距离) ,只要一个子问题返回真,就可以停止计算

    代码如下:

     1 class Solution {
     2 public:
     3     bool isScramble(string s1, string s2) {
     4         //动态规划解法
     5         if(s1.size() != s2.size())return false;
     6         const int len = s1.size();
     7         //dp[k][i][j]表示s2从j开始长度为k的子串是否可以由s1从i开始长度为k的子串转换而成
     8         bool dp[len+1][len][len];
     9         //初始化长度为1的子串的dp值
    10         for(int i = 0; i <= len-1; i++)
    11             for(int j = 0; j <= len-1; j++)
    12                 dp[1][i][j] = s1[i] == s2[j] ? true : false;
    13         for(int k = 2; k <= len; k++)//子串的长度
    14             for(int i = 0; i <= len-k; i++)//s1的起始位置
    15                 for(int j = 0; j <= len-k; j++)//s2的起始位置
    16                 {
    17                     dp[k][i][j] = false;
    18                     //divlen表示两个子串分割点到子串起始端的距离
    19                     for(int divlen = 1; divlen < k && !dp[k][i][j]; divlen++)
    20                         dp[k][i][j] = (dp[divlen][i][j] && dp[k-divlen][i+divlen][j+divlen])
    21                             || (dp[divlen][i][j+k-divlen] && dp[k-divlen][i+divlen][j]);
    22                 }
    23         return dp[len][0][0];
    24     }
    25 };

    【版权声明】转载请注明出处:http://www.cnblogs.com/TenosDoIt/p/3452004.html

  • 相关阅读:
    Java多线程之 对同一个值操作
    Java 之 应用多线程计算1+2+...+100之多种方法比较(二)
    Java 之 应用多线程计算1+2+...+100之多种方法比较(一)
    SQL语句优化
    第六章:shiro Realm相关对象
    第五章:shiro密码加密
    第四章:shiro的INI配置
    第三章:shiro授权认证
    第二章:shiro身份验证
    第一章:Shiro简介
  • 原文地址:https://www.cnblogs.com/TenosDoIt/p/3452004.html
Copyright © 2011-2022 走看看