The “Scramble String” problem is a classical recursive problem that deals with determining if one string is a scrambled version of another.
Problem Statement
Given two strings, s1 and s2, check if s2 is a scrambled version of s1.
A scrambled string of s1 can be obtained by recursively applying the following operations:
- Split the string into two non-empty substrings.
- Swap the two substrings.
- Optionally, repeat the process on each substring.
Examples
Example 1:
Input:s1 = "great"
, s2 = "rgeat"
Output:true
Explanation:
- Split
"great"
into"gr"
and"eat"
. - Swap
"gr"
and"eat"
to get"eatgr"
. - Scramble
"gr"
to"rg"
and"eat"
remains unchanged.
Example 2:
Input:s1 = "abcde"
, s2 = "caebd"
Output:false
Example 3:
Input:s1 = "a"
, s2 = "a"
Output:true
Approach and Algorithm
1. Recursive Approach
We recursively check all possible ways to split the strings and validate if either:
- The left part of
s1
is a scramble of the left part ofs2
and the right parts are scrambles of each other. - The left part of
s1
is a scramble of the right part ofs2
and the right part ofs1
is a scramble of the left part ofs2
.
2. Dynamic Programming (DP)
We use a 3D DP table to store results for substrings of s1
and s2
.
Complexity
- Time Complexity: O(N^4), where N is the length of the strings.
- Space Complexity: O(N^3), for the DP table.
Code Implementation
C:
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
bool isScrambleUtil(char *s1, char *s2, int n) {
if (strncmp(s1, s2, n) == 0) return true;
if (n == 1) return s1[0] == s2[0];
int freq[26] = {0};
for (int i = 0; i < n; i++) {
freq[s1[i] - 'a']++;
freq[s2[i] - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (freq[i] != 0) return false;
}
for (int i = 1; i < n; i++) {
if (isScrambleUtil(s1, s2, i) && isScrambleUtil(s1 + i, s2 + i, n - i)) return true;
if (isScrambleUtil(s1, s2 + n - i, i) && isScrambleUtil(s1 + i, s2, n - i)) return true;
}
return false;
}
bool isScramble(char *s1, char *s2) {
int n = strlen(s1);
if (n != strlen(s2)) return false;
return isScrambleUtil(s1, s2, n);
}
int main() {
char s1[] = "great";
char s2[] = "rgeat";
printf("Is Scramble: %s\n", isScramble(s1, s2) ? "true" : "false");
return 0;
}
C++:
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;
unordered_map<string, bool> memo;
bool isScrambleUtil(string s1, string s2) {
if (s1 == s2) return true;
if (s1.size() != s2.size()) return false;
string key = s1 + "#" + s2;
if (memo.find(key) != memo.end()) return memo[key];
int n = s1.size();
int freq[26] = {0};
for (int i = 0; i < n; i++) {
freq[s1[i] - 'a']++;
freq[s2[i] - 'a']--;
}
for (int i = 0; i < 26; i++) {
if (freq[i] != 0) return memo[key] = false;
}
for (int i = 1; i < n; i++) {
if (isScrambleUtil(s1.substr(0, i), s2.substr(0, i)) &&
isScrambleUtil(s1.substr(i), s2.substr(i))) {
return memo[key] = true;
}
if (isScrambleUtil(s1.substr(0, i), s2.substr(n - i)) &&
isScrambleUtil(s1.substr(i), s2.substr(0, n - i))) {
return memo[key] = true;
}
}
return memo[key] = false;
}
bool isScramble(string s1, string s2) {
memo.clear();
return isScrambleUtil(s1, s2);
}
int main() {
string s1 = "great", s2 = "rgeat";
cout << "Is Scramble: " << (isScramble(s1, s2) ? "true" : "false") << endl;
return 0;
}
Java:
import java.util.HashMap;
import java.util.Map;
public class ScrambleString {
static Map<String, Boolean> memo = new HashMap<>();
public static boolean isScramble(String s1, String s2) {
if (s1.equals(s2)) return true;
if (s1.length() != s2.length()) return false;
String key = s1 + "#" + s2;
if (memo.containsKey(key)) return memo.get(key);
int n = s1.length();
int[] freq = new int[26];
for (int i = 0; i < n; i++) {
freq[s1.charAt(i) - 'a']++;
freq[s2.charAt(i) - 'a']--;
}
for (int f : freq) {
if (f != 0) return memo.put(key, false);
}
for (int i = 1; i < n; i++) {
if (isScramble(s1.substring(0, i), s2.substring(0, i)) &&
isScramble(s1.substring(i), s2.substring(i))) {
return memo.put(key, true);
}
if (isScramble(s1.substring(0, i), s2.substring(n - i)) &&
isScramble(s1.substring(i), s2.substring(0, n - i))) {
return memo.put(key, true);
}
}
return memo.put(key, false);
}
public static void main(String[] args) {
String s1 = "great", s2 = "rgeat";
System.out.println("Is Scramble: " + isScramble(s1, s2));
}
}
Python:
def isScramble(s1, s2):
if s1 == s2:
return True
if sorted(s1) != sorted(s2):
return False
n = len(s1)
for i in range(1, n):
if (isScramble(s1[:i], s2[:i]) and isScramble(s1[i:], s2[i:])) or \
(isScramble(s1[:i], s2[-i:]) and isScramble(s1[i:], s2[:-i])):
return True
return False
# Example Usage
s1 = "great"
s2 = "rgeat"
print("Is Scramble:", isScramble(s1, s2))
JavaScript:
function isScramble(s1, s2) {
if (s1 === s2) return true;
if (s1.split("").sort().join("") !== s2.split("").sort().join("")) return false;
let n = s1.length;
for (let i = 1; i < n; i++) {
if (
(isScramble(s1.substring(0, i), s2.substring(0, i)) &&
isScramble(s1.substring(i), s2.substring(i))) ||
(isScramble(s1.substring(0, i), s2.substring(n - i)) &&
isScramble(s1.substring(i), s2.substring(0, n - i)))
) {
return true;
}
}
return false;
}
// Example Usage
let s1 = "great";
let s2 = "rgeat";
console.log("Is Scramble:", isScramble(s1, s2));
C#
using System;
using System.Collections.Generic;
public class ScrambleString
{
// Memoization dictionary to store previously computed results
static Dictionary<string, bool> memo = new Dictionary<string, bool>();
// Helper function to check if one string is a scramble of the other
public static bool IsScramble(string s1, string s2)
{
// If both strings are equal, no need to check further
if (s1 == s2)
return true;
// If the lengths of the strings are not the same, return false immediately
if (s1.Length != s2.Length)
return false;
// Create a key for memoization from the two strings
string key = s1 + "#" + s2;
// If the result for this pair of strings is already computed, return it
if (memo.ContainsKey(key))
return memo[key];
// Check if both strings contain the same characters in the same frequency
int[] freq = new int[26];
for (int i = 0; i < s1.Length; i++)
{
freq[s1[i] - 'a']++;
freq[s2[i] - 'a']--;
}
// If the frequencies don't match, they cannot be scrambles
foreach (var f in freq)
{
if (f != 0)
{
memo[key] = false; // Store the result in memoization dictionary
return false;
}
}
// Now, check for the two conditions of scrambled strings:
for (int i = 1; i < s1.Length; i++)
{
// Case 1: No swapping of substrings
if (IsScramble(s1.Substring(0, i), s2.Substring(0, i)) &&
IsScramble(s1.Substring(i), s2.Substring(i)))
{
memo[key] = true; // Memoize the result
return true;
}
// Case 2: Swapping of substrings
if (IsScramble(s1.Substring(0, i), s2.Substring(s2.Length - i)) &&
IsScramble(s1.Substring(i), s2.Substring(0, s2.Length - i)))
{
memo[key] = true; // Memoize the result
return true;
}
}
// If no valid scramble was found, memoize and return false
memo[key] = false;
return false;
}
// Main function to test the scramble string function
public static void Main()
{
string s1 = "great";
string s2 = "rgeat";
Console.WriteLine("Is Scramble: " + IsScramble(s1, s2)); // Output: true
s1 = "abcde";
s2 = "caebd";
Console.WriteLine("Is Scramble: " + IsScramble(s1, s2)); // Output: false
}
}