LeetCode-87.擾亂字符串(Scramble String)

2021-03-02 極客算法

這道題中文版翻譯的已經有一部分提示了,還把樹的結構給畫了出來。英文版新版只給了兩個轉換規則

We can scramble a string s to get a string t using the following algorithm:
If the length of the string is 1, stop.If the length of the string is > 1, do the following:Split the string into two non-empty substrings at a random index, i.e., if the string is s, divide it to x and y where s = x + y.Randomly decide to swap the two substrings or to keep them in the same order. i.e., after this step, s may become s = x + y or s = y + x.Apply step 1 recursively on each of the two substrings x and y.Given two strings s1 and s2 of the same length, return true if s2 is a scrambled string of s1, otherwise, return false.

87. 擾亂字符串

給定一個字符串 s1,我們可以把它遞歸地分割成兩個非空子字符串,從而將其表示為二叉樹。

下圖是字符串 s1 = "great" 的一種可能的表示形式。

    great   /    \  gr    eat / \    /  \g   r  e   at           / \          a   t

在擾亂這個字符串的過程中,我們可以挑選任何一個非葉節點,然後交換它的兩個子節點。

例如,如果我們挑選非葉節點 "gr" ,交換它的兩個子節點,將會產生擾亂字符串 "rgeat" 。

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

我們將 "rgeat」 稱作 "great" 的一個擾亂字符串。

同樣地,如果我們繼續交換節點 "eat" 和 "at" 的子節點,將會產生另一個新的擾亂字符串 "rgtae" 。

    rgtae   /    \  rg    tae / \    /  \r   g  ta  e       / \      t   a

我們將 "rgtae」 稱作 "great" 的一個擾亂字符串。

給出兩個長度相等的字符串 s1 和 s2,判斷 s2 是否是 s1 的擾亂字符串。

示例 1:

輸入: s1 = "great", s2 = "rgeat"輸出: true

示例 2:

輸入: s1 = "abcde", s2 = "caebd"輸出: false

來源:力扣(LeetCode)

連結:https://leetcode-cn.com/problems/scramble-string

Link:https://leetcode.com/problems/scramble-string/

暴力破解

O(5^N)

如果切割,使得原字符串與目標字符串分別左右相等或者交叉相等, 就說明能夠轉換成功

這裡需要保證,每次遞歸調用函數,兩個入參長度相等,len(s1) == len(s2)

# 分割不交換left     right
| |
left right
# 分割交換left | right \ / X / \left | right

代碼實現
class Solution:    def isScramble(self, s1: str, s2: str) -> bool:        if s1 == s2:            return True
length = len(s1) for k in range(1, length): if self.isScramble(s1[0 : k], s2[0 : k]) and self.isScramble(s1[k : length], s2[k : length]): return True
if self.isScramble(s1[0 : k], s2[length - k : length]) and self.isScramble(s1[k : length], s2[0 : length - k]): return True

時間複雜度
# for循環中判斷時間 = T(K) + T(N - K), 一共有兩個判斷x2T(N) = for k in 1...n-1: 2 * (T(k) + T(N - k))
= 2(T(1) + T(N - 1) + T(2) + T(N - 2) + ... + T(N - 1) + T(1))
= 2 * 2(T(1) + T(2) + ... + T(N - 1))
= 4(T(1) + T(2) + ... + T(N - 1))
# 同理, 替換下面左邊部分T(N - 1) = 4(T(1) + T(2) + ... + T(N - 2))
= 4(T(1) + T(2) + ... + T(N - 2)) + 4T(N - 1)
= T(N - 1) + 4T(N - 1)
= 5T(N - 1)
# 所以T(N) = 5 * T(N - 1) = 5^2 * T(N - 2) = 5 ^ N * T(1) = 5^N

遞歸 + 記憶化搜索 + 剪枝

剪枝: 只是在暴力搜索的過程中,如果能夠提前判斷結果,比如"aab", "aac"裡面字符個數不相等, 就不用再判斷了

記憶化搜索: 是指可能出現重複的調用過程,使用緩存來記錄(入參1, 入參2) -> (結果), 就不用再計算一遍了

理論上: 搜索 + 記憶化搜索 = 動態規劃

⚠️剪枝能夠提高搜索效率,但是理論的時間複雜度並沒有減少,可以假設一個Case,每次判斷都能通過剪枝攔截

from functools import lru_cachefrom collections import Counterclass Solution:    @lru_cache(maxsize=None) # 記憶化搜索    def isScramble(self, s1: str, s2: str) -> bool:        if s1 == s2:            return True
if Counter(s1) != Counter(s2): return False
length = len(s1) for k in range(1, length): if self.isScramble(s1[0 : k], s2[0 : k]) and self.isScramble(s1[k : length], s2[k : length]): return True
if self.isScramble(s1[0 : k], s2[length - k : length]) and self.isScramble(s1[k : length], s2[0 : length - k]): return True

動態規劃狀態定義

dp[i][j][k], 定義同長度的s1[i : i + k], s2[j : j + k]是否可以成功轉換

狀態轉換
# 左右分別可轉換s1 [    left1    |        right1        ]   i             i + cut                i + k -1
| |
s2 [ left2 | right2 ] j j + cut j + k - 1

dp[i][j][k] = (dp[i][j][cut] and dp[i + cut][j + cut][k - cut])
# 左右交叉可轉換s1 [ left1 | right1 ] i i + cut i + k -1
\ / X / \
s2 [ left2 | right2 ] j j + k - cut j + k - 1

dp[i][j][k] = (dp[i][j + k - cut][cut] and dp[i + cut][j][k - cut])

計算方向

字符串k = 1到N

邊界條件

長度k從1開始,所以開數組的時候要多一位

當k = 1時,只要判斷單個字符串是否相等即可

class Solution:    def isScramble(self, s1: str, s2: str) -> bool:        if len(s1) != len(s2):            return False
length = len(s1) dp = [[[False for _ in range(length + 1)] for _ in range(length)] for _ in range(length)]
for i in range(length): for j in range(length): dp[i][j][1] = (s1[i] == s2[j])
for k in range(2, length + 1): for i in range(length - k + 1): for j in range(length - k + 1): for cut in range(1, k - 1 + 1):
if (dp[i][j][cut] and dp[i + cut][j + cut][k - cut]): dp[i][j][k] = True break
if (dp[i][j + k - cut][cut] and dp[i + cut][j][k - cut]): dp[i][j][k] = True break
return dp[0][0][length]

--End--

相關焦點

  • [LeetCode] 943. Find the Shortest Superstring 找到最短的超級字符串
    A,讓我們找一個最短的字符串,使得所有A中的字符串是都該字符串的子串,並給了兩個例子。首先來解決復用字符個數計算的問題,為了避免重複計算,可以直接計算任意兩個字符串之間的復用字符的個數,用一個二維數組 overlap 表示,其中 overlap[i][j] 表示字符串 A[i] 和 A[j] 之間的復用字符的個數,比如 abc 和 bca 的復用個數是2,注意 overlap[j][i] 不一定等於 overlap[i][j],比如 bca 和 abc 的復用字符個數就是1個。
  • 【每日leetcode】左旋轉字符串
    題目字符串的左旋轉操作是把字符串前面的若干個字符轉移到字符串的尾部。請定義一個函數實現字符串左旋轉操作的功能。比如,輸入字符串"abcdefg"和數字2,該函數將返回左旋轉兩位得到的結果"cdefgab"。
  • 無重複字符的最長子串 | Leetcode題解
    示例 1:輸入: "abcabcbb"輸出: 3解釋: 因為無重複字符的最長子串是 "abc",所以其長度為 3。示例 2:輸入: "bbbbb"輸出: 1解釋: 因為無重複字符的最長子串是 "b",所以其長度為 1。
  • AK leetcode 流浪計劃 - 回文串
    題目連結:https://leetcode-cn.com/problems/valid-palindrome/題目大意給定一個字符串,驗證它是否是回文串,只考慮字母和數字字符,可以忽略字母的大小寫。://leetcode-cn.com/problems/can-make-palindrome-from-substring/題目大意給你一個字符串 s,請你對 s 的子串進行檢測。
  • String字符串常用方法
    1、IndexOf方法:確定指定字符串在字符串中的索引,如果在字符串中找到指定字符,則返回其索引,否則返回-1。
  • leetcode之字符串相加
    序本文主要記錄一下leetcode之字符串相加題目給定兩個字符串形式的非負整數 num1 和num2 ,計算它們的和。
  • R 字符串之 stringr
    R 字符串之 stringr前言昨天我們介紹 R 數據處理的時候,對字符串的操作都是用自帶的函數。雖然 R 的字符串並不是它的強項,看起來也不是那麼的優雅,但是字符串在數據處理和清洗過程中還是扮演者較為重要的角色。
  • string字符串的比較
    string字符串的比較運算符有如下幾種:>大於;>=大於等於;<小於小於等於;==等於;如下所示是等於運算符的一個實例:#include<iostream>#include<windows.h>#include<string
  • C/C++中字符串string類型
    瀏覽器版本過低,暫不支持視頻播放字符串型作用:用於表示一串字符>兩種風格1.C風格字符串: char 變量名[] = "字符串值"示例:int main() {char str1[] = "hello world";
  • 圖解LeetCode第 3 號問題:無重複字符的最長子串
    題目描述 給定一個字符串,找出不含有重複字符的最長子串的長度。示例 1:  輸入: "abcabcbb"   輸出: 3   解釋: 無重複字符的最長子串是 "abc",其長度為 3。示例 2:  輸入: "bbbbb"  輸出: 1.     解釋: 無重複字符的最長子串是 "b",其長度為 1。
  • Tcl學習:string compare命令對字符串的比較
    打開APP Tcl學習:string compare命令對字符串的比較 工程師李察 發表於 2018-09-23 10:10:00
  • L2-數據結構-第05課 字符串string
    L2-數據結構-第05課 字符串stringstringstring 類型支持長度可變的字符串,C++ 標準庫將負責管理與存儲字符相關的內存,以及提供各種有用的操作底層:是一種順序表的結構,元素是char類型的字符頭文件#
  • 【leetcode】 345. Reverse Vowels of a String && 1. Two Sum
    反轉字符串中的元音字母Write a function that takes a string as input and reverse only the vowels of a string.編寫一個函數,以字符串作為輸入,反轉該字符串中的元音字母。
  • Hive函數大全(含例子)之字符串函數(String Functions)
    字符串函數 String Functionsascii(string str)返回結果: 返回字符串str首字母的十進位ascii碼返回類型: int('Aa'); -- 結果為 aalpad(string str, int len, string pad)返回結果: 使用pad填充字符串str的左邊,使其長度變為len;如果字符串str
  • 為什麼 Python 的 f-string 可以連接字符串與數字?
    由此,我們要引出一個問題:如何在不作顯式類型轉化的情況下,進行字符串與數字類型的拼接呢?在《詳解Python拼接字符串的七種方式》這篇文章中,它梳理了七種拼接字符串的寫法,我們可以逐個來試驗一下。但是,現在再看看最後一種寫法,也就是 f-string 寫法,似乎就不是那麼明顯了。首先,在字符串內部,它並沒有像「%格式化」那樣指定佔位符的類型;其次,所要拼接的數字並沒有作為任何函數的參數來傳遞。
  • 【Golang】快速複習指南QuickReview(一)——字符串string
    String-字符串1.C#的字符串 字符串在C#中,是一個特殊的類型,不能簡單把它歸納為值類型,或者引用類型。需要記住的有兩點:1.無論對字符串做什麼操作,都會在內存中生成一個新的實例,即使是一個簡單的重新賦值操作。
  • 最長回文子串 | Leetcode題解
    題目描述給定一個字符串 s,找到 s 中最長的回文子串。你可以假設  s 的最大長度為 1000。5.longest-palindromic-substring解決這類問題的核心思想就是兩個字「延伸」,具體來說如果在一個不是回文字符串的字符串兩端添加任何字符,或者在回文串左右分別加不同的字符,得到的一定不是回文串
  • 經典leetcode算法題分享(字符串)
    反轉字符串題目:編寫一個函數,其作用是將輸入的字符串反轉過來。輸入字符串以字符數組 char[] 的形式給出。示例:s = "leetcode"返回 0s = "loveleetcode"返回 2提示:你可以假定該字符串只包含小寫字母。
  • 最長公共前綴 | Leetcode題解
    題目描述:編寫一個函數來查找字符串數組中的最長公共前綴。如果不存在公共前綴,返回空字符串 "" 。難度:支持語言:JavaScript、Python、C++、Java相關標籤相關企業複雜度分析時間複雜度:O(mn)O(mn),其中 mm 是字符串數組中的字符串的平均長度,nn 是字符串的數量。
  • LeetCode刷題筆記 - 3. 無重複字符的最長子串
    官方連結:https://leetcode-cn.com/problemset/all/一、題意難度:中等https://leetcode-cn.com/problems/longest-substring-without-repeating-characters