Rearrange K Substrings to Form Target String
Problem
You are given two strings s and t, both of which are anagrams of each other, and an integer k.
Your task is to determine whether it is possible to split the string s into
k equal-sized substrings, rearrange the substrings, and concatenate them in any order to create a new string that matches the given string t.
Return true if this is possible, otherwise, return false.
An anagram is a word or phrase formed by rearranging the letters of a different word or phrase, using all the original letters exactly once.
A substring is a contiguous non-empty sequence of characters within a string.
Examples
Example 1
Input: s = "abcd", t = "cdab", k = 2
Output: true
Explanation:
* Split `s` into 2 substrings of length 2: `["ab", "cd"]`.
* Rearranging these substrings as `["cd", "ab"]`, and then concatenating them results in `"cdab"`, which matches `t`.
Example 2
Input: s = "aabbcc", t = "bbaacc", k = 3
Output: true
Explanation:
* Split `s` into 3 substrings of length 2: `["aa", "bb", "cc"]`.
* Rearranging these substrings as `["bb", "aa", "cc"]`, and then concatenating them results in `"bbaacc"`, which matches `t`.
Example 3
Input: s = "aabbcc", t = "bbaacc", k = 2
Output: false
Explanation:
* Split `s` into 2 substrings of length 3: `["aab", "bcc"]`.
* These substrings cannot be rearranged to form `t = "bbaacc"`, so the output is `false`.
Constraints
1 <= s.length == t.length <= 2 * 10^51 <= k <= s.lengths.lengthis divisible byk.sandtconsist only of lowercase English letters.- The input is generated such that
sandtare anagrams of each other.
Solution
Method 1 – Multiset Matching of Substrings
Intuition
Since we can rearrange the k substrings, we only need to check if the multiset of substrings of s (split into k equal parts) matches the multiset of substrings of t (split the same way).
Approach
- Split s and t into k equal-length substrings.
- Count the frequency of each substring in both s and t.
- If the multisets match, return true; otherwise, return false.
Code
C++
#include <unordered_map>
class Solution {
public:
bool canRearrangeSubstrings(string s, string t, int k) {
int n = s.size(), m = n / k;
unordered_map<string, int> cnt1, cnt2;
for (int i = 0; i < n; i += m) cnt1[s.substr(i, m)]++;
for (int i = 0; i < n; i += m) cnt2[t.substr(i, m)]++;
return cnt1 == cnt2;
}
};
Go
func canRearrangeSubstrings(s, t string, k int) bool {
n, m := len(s), len(s)/k
cnt1, cnt2 := map[string]int{}, map[string]int{}
for i := 0; i < n; i += m { cnt1[s[i:i+m]]++ }
for i := 0; i < n; i += m { cnt2[t[i:i+m]]++ }
if len(cnt1) != len(cnt2) { return false }
for k, v := range cnt1 { if cnt2[k] != v { return false } }
return true
}
Java
import java.util.*;
class Solution {
public boolean canRearrangeSubstrings(String s, String t, int k) {
int n = s.length(), m = n / k;
Map<String, Integer> cnt1 = new HashMap<>(), cnt2 = new HashMap<>();
for (int i = 0; i < n; i += m) cnt1.merge(s.substring(i, i+m), 1, Integer::sum);
for (int i = 0; i < n; i += m) cnt2.merge(t.substring(i, i+m), 1, Integer::sum);
return cnt1.equals(cnt2);
}
}
Kotlin
class Solution {
fun canRearrangeSubstrings(s: String, t: String, k: Int): Boolean {
val n = s.length; val m = n / k
val cnt1 = mutableMapOf<String, Int>()
val cnt2 = mutableMapOf<String, Int>()
for (i in 0 until n step m) cnt1[s.substring(i, i+m)] = cnt1.getOrDefault(s.substring(i, i+m), 0) + 1
for (i in 0 until n step m) cnt2[t.substring(i, i+m)] = cnt2.getOrDefault(t.substring(i, i+m), 0) + 1
return cnt1 == cnt2
}
}
Python
class Solution:
def canRearrangeSubstrings(self, s: str, t: str, k: int) -> bool:
n, m = len(s), len(s) // k
from collections import Counter
cnt1 = Counter(s[i:i+m] for i in range(0, n, m))
cnt2 = Counter(t[i:i+m] for i in range(0, n, m))
return cnt1 == cnt2
Rust
use std::collections::HashMap;
impl Solution {
pub fn can_rearrange_substrings(s: String, t: String, k: i32) -> bool {
let n = s.len();
let m = n / k as usize;
let mut cnt1 = HashMap::new();
let mut cnt2 = HashMap::new();
for i in (0..n).step_by(m) {
*cnt1.entry(&s[i..i+m]).or_insert(0) += 1;
*cnt2.entry(&t[i..i+m]).or_insert(0) += 1;
}
cnt1 == cnt2
}
}
TypeScript
class Solution {
canRearrangeSubstrings(s: string, t: string, k: number): boolean {
const n = s.length, m = n / k;
const cnt1 = new Map<string, number>(), cnt2 = new Map<string, number>();
for (let i = 0; i < n; i += m) cnt1.set(s.slice(i, i+m), (cnt1.get(s.slice(i, i+m))||0)+1);
for (let i = 0; i < n; i += m) cnt2.set(t.slice(i, i+m), (cnt2.get(t.slice(i, i+m))||0)+1);
if (cnt1.size !== cnt2.size) return false;
for (const [k, v] of cnt1) if (cnt2.get(k) !== v) return false;
return true;
}
}
Complexity
- ⏰ Time complexity:
O(n), where n is the length of s, since we split and count substrings in linear time. - 🧺 Space complexity:
O(n), for storing the substring counts.