We can scramble a string s to get a string t using the following algorithm:
s, divide it to x and y where s = x + y.s may become s = x + y or s = y + x.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.
Example 1:
Input: s1 = "great", s2 = "rgeat" Output: true Explanation: One possible scenario applied on s1 is: "great" --> "gr/eat" // divide at random index. "gr/eat" --> "gr/eat" // random decision is not to swap the two substrings and keep them in order. "gr/eat" --> "g/r / e/at" // apply the same algorithm recursively on both substrings. divide at random index each of them. "g/r / e/at" --> "r/g / e/at" // random decision was to swap the first substring and to keep the second substring in the same order. "r/g / e/at" --> "r/g / e/ a/t" // again apply the algorithm recursively, divide "at" to "a/t". "r/g / e/ a/t" --> "r/g / e/ a/t" // random decision is to keep both substrings in the same order. The algorithm stops now, and the result string is "rgeat" which is s2. As one possible scenario led s1 to be scrambled to s2, we return true.
Example 2:
Input: s1 = "abcde", s2 = "caebd" Output: false
Example 3:
Input: s1 = "a", s2 = "a" Output: true
Constraints:
s1.length == s2.length1 <= s1.length <= 30s1 and s2 consist of lowercase English letters.Scramble String asks whether one string can be transformed into another by recursively partitioning and swapping substrings. A brute force recursive approach tries every possible split position and checks both cases: swapped and non-swapped substrings. However, this leads to heavy recomputation.
The optimized strategy uses recursion with memoization or dynamic programming. For two substrings of equal length, we attempt all partition points and verify if their left and right segments match either directly or after swapping. Memoization stores results for previously checked substring pairs to avoid repeated work.
Another common method is a 3D DP table where dp[i][j][len] represents whether the substring starting at index i in the first string can form the substring starting at index j in the second string with length len. By iterating through substring lengths and split points, we progressively determine valid scrambles.
This approach significantly reduces redundant checks while maintaining manageable complexity.
| Approach | Time Complexity | Space Complexity |
|---|---|---|
| Recursion + Memoization | O(n^4) | O(n^3) |
| Bottom-Up Dynamic Programming | O(n^4) | O(n^3) |
Aditya Verma
This approach uses recursion to explore all possible valid splits and checks if either of the permutations exist. To avoid re-computation, use a dictionary to memoize already computed results for specific substrings.
Time Complexity: O(N^4), where N is the length of the strings.
Space Complexity: O(N^3), used by the memoization array.
1class Solution:
2 def __init__(self):
3 self.memo = {}
4
5 def isScramble(self, s1: str, s2: str) -> bool:
6 if s1 == s2:
7 return True
8 if (s1, s2) in self.memo:
9 return self.memo[(s1, s2)]
10 if sorted(s1) != sorted(s2):
11 return False
12 n = len(s1)
13 for i in range(1, n):
14 if (self.isScramble(s1[:i], s2[:i]) and self.isScramble(s1[i:], s2[i:])) or \
15 (self.isScramble(s1[:i], s2[-i:]) and self.isScramble(s1[i:], s2[:-i])):
16 self.memo[(s1, s2)] = True
17 return True
18 self.memo[(s1, s2)] = False
19 return FalseThis Python solution uses recursion with memoization, checking all possible partition indices and both possible scrambling options. It uses a dictionary for memoization, speeding up repeated queries. Sorting ensures a quick early rejection of incongruent strings.
This approach is based on dynamic programming. We set up a 3D table where table[i][j][k] holds true if s1's substring starting at index i with length k is a scrambled string of s2's substring starting at index j.
Time Complexity: O(N^4), where N is the length of the strings.
Space Complexity: O(N^3), due to the 3D DP table.
1using System;
2
public class Solution {
public bool IsScramble(string s1, string s2) {
int n = s1.Length;
if (s1 == s2) return true;
char[] sorted1 = s1.ToCharArray();
Array.Sort(sorted1);
char[] sorted2 = s2.ToCharArray();
Array.Sort(sorted2);
if (!new String(sorted1).Equals(new String(sorted2))) return false;
bool[,,] dp = new bool[n, n, n + 1];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i, j, 1] = s1[i] == s2[j];
}
}
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
for (int j = 0; j <= n - len; j++) {
for (int k = 1; k < len; k++) {
if ((dp[i, j, k] && dp[i + k, j + k, len - k]) ||
(dp[i, j + len - k, k] && dp[i + k, j, len - k])) {
dp[i, j, len] = true;
break;
}
}
}
}
}
return dp[0, 0, n];
}
}Watch expert explanations and walkthroughs
Practice problems asked by these companies to ace your technical interviews.
Explore More ProblemsJot down your thoughts, approach, and key learnings
Yes, Scramble String is considered a classic hard dynamic programming problem and can appear in advanced interview rounds. It tests recursion, DP optimization, and understanding of substring partitioning strategies.
The optimal approach uses recursion with memoization or a bottom-up dynamic programming solution. By storing results for substring comparisons, it avoids redundant computations and significantly improves efficiency over naive recursion.
The difficulty comes from the large number of possible substring partitions and swap combinations. Efficiently pruning repeated computations using memoization or DP is essential to make the solution feasible.
Dynamic programming tables or hash maps for memoization work best. They store intermediate results for substring pairs, allowing the algorithm to reuse previously computed states efficiently.
The C# approach is similar to the Java counterpart, using a three-dimensional array to capture scramble possibilities. Early validation of character sequence compatibility is performed to cut down processing time and enhance efficiency.