題目描述
給定一個字符串 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
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
白話題目:
算法:
詳細解釋關注 B站 【C語言全代碼】學渣帶你刷Leetcode 不走丟 https://www.bilibili.com/video/BV1C7411y7gB
C語言完全代碼
//方法二:動態規劃算法
//1,遞歸是自頂向下處理,不斷將問題分解遞歸,複雜度高,超時
//2,用數組保存遞歸過程中的值,將處理問題的方式改爲 自底向上處理
//3,把 自頂向下 改成 自底向上 需要解決如何找到最小值的問題,即最先計算什麼
//4,遞歸函數傳遞過程中處理了三個元素 (i, j, len) 分別爲 s1下標,s2下標,分割長度
//5,使用動態數組 dp[i][j][l] 表示 &s1[i] 和 &s2[j] 長度爲 l 個字符是否爲擾亂字符串
//6,計算初始值 當 l=1 時 if(s1[i]==s2[j]) dp[i][j][1]=true
//7,三維的動態規劃數組不太好理解,可以先理解幾個例子
/*
以 great 和 rgeat 爲例
例1:
當 l == 2 時
dp[0][0][2] = (dp[0][0][1] && dp[1][1][1]) || (dp[0][1][1] && dp[1][0][1])
...
dp[3][3][2] = (dp[3][3][1] && dp[4][4][1]) || (dp[3][4][1] && dp[4][3][1])
當 l == 3 時
dp[0][0][3] = ((dp[0][0][1] && dp[1][1][2]) || (dp[0][2][1] && dp[1][0][2])) ||
((dp[0][0][2] && dp[2][2][1]) || (dp[0][1][2] && dp[2][0][1]))
...
dp[2][2][3] = ((dp[2][2][1] && dp[3][3][2]) || (dp[2][4][1] && dp[3][2][2])) ||
((dp[2][2][2] && dp[4][4][1]) || (dp[2][3][2] && dp[4][2][1]))
*/
bool isScramble(char * s1, char * s2){
int i = 0;
int j = 0;
int l = 0;
int k = 0;
int iLenS1 = strlen(s1);
int iLenS2 = strlen(s2);
bool bRet = false;
bool dp[iLenS1][iLenS2][iLenS1 + 1];
if((NULL == s1) || (NULL == s2) || (iLenS1 != iLenS2)) return false;
//1,初始化動態規劃數組
memset(dp, 0x00, sizeof(bool) * iLenS1 * iLenS2 * (iLenS1 + 1));
for(i = 0; i < iLenS1; i++)
{
for(j = 0; j < iLenS2; j++)
{
if(s1[i] == s2[j])
{
dp[i][j][1] = true;
}
}
}
//2,動態規劃處理
for(l = 2; l <= iLenS1; l++)
{
//依次處理分割長度
for(i = 0; i <= iLenS1 - l; i++)
{
for(j = 0; j <= iLenS2 - l; j++)
{
//計算 dp[i][j][l] 的值, 需要判斷 (1, l) 範圍任一分割長度是否能判定爲擾亂字符串
for(k = 1; k < l; k++)
{
// printf("[1][i=%d][j=%d][l=%d][k=%d][%d][%d]\n", i, j, l, k, dp[i][j][k], dp[i + k][j + k][l - k]);
//判斷不交換節點
if((dp[i][j][k]) && (dp[i + k][j + k][l - k]))
{
dp[i][j][l] = true;
break;
}
// printf("[2][i=%d][j=%d][l=%d][k=%d][%d][%d]\n", i, j, l, k, dp[i][j + l - k][k], dp[i + k][j][l - k]);
//判斷交換節點
if((dp[i][j + l - k][k]) && (dp[i + k][j][l - k]))
{
dp[i][j][l] = true;
break;
}
}
}
}
}
return dp[0][0][iLenS1];
}
/*
//方法一:遞歸法(超時)
//1,求最終解 fun(s1(0, len), s2(0, len)) 表示字符串s1,和s2是否匹配爲擾亂字符串
//2,遞歸算法
//3,按照字符串長度遍歷 k = (1, len)
//4, fun(s1(0, len), s2(0, len)) =
// (fun(s1(0, k), s2(0, k)) && fun(s1(k, len), s2(k, len))) ||
// (fun(s1(0, k), s2(len - k, len)) && fun(s1(k, len), s2(0, len - k)))
//遞歸函數
bool recursiveScramble(char* s1, char* s2, int len){
int i = 0;
bool bRet = false;
// printf("[1][s1=%s][s2=%s][len=%d]\n", s1, s2, len);
//1,結束處理
if(len == 1)
{
if(s1[0] == s2[0]) return true;
}
else
{
//2,遍歷長度回溯處理
for(i = 1; i < len; i++)
{
//不交換處理
bRet = recursiveScramble(&s1[0], &s2[0], i);
// printf("[2][s1=%s][s2=%s][i=%d][ret=%d]\n", &s1[0], &s2[0], i, bRet);
if(bRet)
{//great
bRet &= recursiveScramble(&s1[i], &s2[i], len - i);
// printf("[3][s1=%s][s2=%s][len=%d][ret=%d]\n", &s1[i], &s2[i], len - i, bRet);
if(bRet) return true;
}
//交換處理
bRet = recursiveScramble(&s1[0], &s2[len - i], i);
// printf("[4][s1=%s][s2=%s][len=%d][ret=%d]\n", &s1[0], &s2[len - i], i, bRet);
if(bRet)
{//great
bRet &= recursiveScramble(&s1[i], &s2[0], len - i);
// printf("[5][s1=%s][s2=%s][len=%d][ret=%d]\n", &s1[i], &s2[0], len - i, bRet);
if(bRet) return true;
}
}
}
return bRet;
}
bool isScramble(char * s1, char * s2){
int iLenS1 = strlen(s1);
int iLenS2 = strlen(s2);
bool bRet = false;
if((NULL == s1) || (NULL == s2) || (iLenS1 != iLenS2)) return false;
bRet = recursiveScramble(s1, s2, iLenS1);
return bRet;
}
*/