Rearrange K Substrings to Form Target String
MedPrompt
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.
Example 1:
Input: s = "abcd", t = "cdab", k = 2
Output: true
Explanation:
- Split
sinto 2 substrings of length 2:["ab", "cd"]. - Rearranging these substrings as
["cd", "ab"], and then concatenating them results in"cdab", which matchest.
Example 2:
Input: s = "aabbcc", t = "bbaacc", k = 3
Output: true
Explanation:
- Split
sinto 3 substrings of length 2:["aa", "bb", "cc"]. - Rearranging these substrings as
["bb", "aa", "cc"], and then concatenating them results in"bbaacc", which matchest.
Example 3:
Input: s = "aabbcc", t = "bbaacc", k = 2
Output: false
Explanation:
- Split
sinto 2 substrings of length 3:["aab", "bcc"]. - These substrings cannot be rearranged to form
t = "bbaacc", so the output isfalse.
Constraints:
1 <= s.length == t.length <= 2 * 1051 <= k <= s.lengths.lengthis divisible byk.sandtconsist only of lowercase English letters.- The input is generated such that
sandtare anagrams of each other.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach tackles the problem by treating the substrings from s and t as two multisets. The core idea is that if two multisets are identical, their sorted versions will also be identical. We generate the substrings for both s and t, sort them, and then perform a direct comparison.
Algorithm
-
- Calculate the substring length
subLen = s.length() / k.
- Calculate the substring length
-
- Create an empty list
s_substrings.
- Create an empty list
-
- Split
sintoksubstrings of lengthsubLenand add them tos_substrings.
- Split
-
- Create an empty list
t_substrings.
- Create an empty list
-
- Split
tintoksubstrings of lengthsubLenand add them tot_substrings.
- Split
-
- Sort the
s_substringslist.
- Sort the
-
- Sort the
t_substringslist.
- Sort the
-
- Return
trueif the sorted lists are identical, otherwise returnfalse.
- Return
Walkthrough
The problem asks if the multiset of k substrings from s can be rearranged to form t. This is equivalent to checking if the multiset of substrings from s is identical to the multiset of substrings from t.
A straightforward way to compare two multisets is to convert them to sorted lists and check for equality.
The algorithm proceeds as follows:
- Calculate the length of each of the
ksubstrings:subLen = s.length() / k. - Create two lists,
s_subsandt_subs. - Iterate through
sandtwith a step size ofsubLen, extracting each substring and adding it to the respective list. - Sort both
s_subsandt_subslexicographically. - Finally, compare the two sorted lists. If they are equal, it means
sandtare composed of the same substrings, and we returntrue. Otherwise, we returnfalse.
import java.util.ArrayList;import java.util.Collections;import java.util.List; class Solution { public boolean canRearrange(String s, String t, int k) { int n = s.length(); // The problem guarantees s.length() is divisible by k. int subLen = n / k; List<String> s_subs = new ArrayList<>(k); for (int i = 0; i < n; i += subLen) { s_subs.add(s.substring(i, i + subLen)); } List<String> t_subs = new ArrayList<>(k); for (int i = 0; i < n; i += subLen) { t_subs.add(t.substring(i, i + subLen)); } Collections.sort(s_subs); Collections.sort(t_subs); return s_subs.equals(t_subs); }}Complexity
Time
`O(n * log(k))`, where `n` is the length of the strings and `k` is the number of substrings. Generating the substrings takes `O(n)`. Sorting a list of `k` strings, where each string comparison takes `O(n/k)`, results in a total sorting time of `O(k * log(k) * (n/k)) = O(n * log(k))`.
Space
`O(n)`. We need two lists to store the substrings. Each list contains `k` substrings of length `n/k`, so the total space required is `2 * k * (n/k) = O(n)`.
Trade-offs
Pros
Conceptually simple and relies on standard sorting and comparison logic.
Easy to implement using built-in list and sorting functionalities.
Cons
The time complexity is dominated by sorting, making it less efficient than a hash-based approach.
Requires storing all substrings for both
sandtin memory, leading toO(n)space usage.
This optimized approach uses a hash map to efficiently count the frequencies of substrings. It first builds a frequency map for all substrings of s. Then, it iterates through the substrings of t, checking if each one can be 'accounted for' by the map. This avoids the overhead of sorting.
Algorithm
-
- Calculate the substring length
subLen = s.length() / k.
- Calculate the substring length
-
- Create a hash map
sub_countsto store substring frequencies.
- Create a hash map
-
- Iterate through
s, splitting it into substrings of lengthsubLen. For each substring, increment its count insub_counts.
- Iterate through
-
- Iterate through
t, splitting it into substrings of lengthsubLen. For each substringsub_t:
- a. Get the current count of
sub_tfrom the map. - b. If the count is null or zero, return
false. - c. Decrement the count of
sub_tin the map.
- Iterate through
-
- If the loop completes, return
true.
- If the loop completes, return
Walkthrough
This method also relies on the principle of comparing the multisets of substrings from s and t. However, it uses a hash map for a more efficient comparison.
The algorithm is as follows:
- Calculate the substring length
subLen = s.length() / k. - Create a
HashMap<String, Integer>to store the frequency of each unique substring found ins. - Iterate through
s, slicing it into substrings of lengthsubLen. For each substring, increment its count in the hash map. - Iterate through
t, also slicing it into substrings. For each substring fromt:- Check if the substring exists as a key in the map with a count greater than zero.
- If it doesn't, it means
tcontains a substring not available ins(or not enough copies of it), so we returnfalse. - If it does, we decrement its count in the map to signify that one instance has been 'used'.
- If the loop over
t's substrings completes successfully, it means every substring intwas matched with one froms. Since the total number of substrings is the same, the multisets must be identical. We can then returntrue.
import java.util.HashMap;import java.util.Map; class Solution { public boolean canRearrange(String s, String t, int k) { int n = s.length(); // The problem guarantees s.length() is divisible by k. int subLen = n / k; Map<String, Integer> subCounts = new HashMap<>(); for (int i = 0; i < n; i += subLen) { String sub = s.substring(i, i + subLen); subCounts.put(sub, subCounts.getOrDefault(sub, 0) + 1); } for (int i = 0; i < n; i += subLen) { String sub = t.substring(i, i + subLen); Integer count = subCounts.get(sub); if (count == null || count == 0) { return false; } subCounts.put(sub, count - 1); } return true; }}Complexity
Time
`O(n)`, where `n` is the string length. Building the map requires iterating through `s`, which involves `k` substring extractions and hash map operations. Each operation takes `O(n/k)` on average (for hashing the substring), leading to a total of `k * O(n/k) = O(n)`. The verification loop for `t` similarly takes `O(n)`.
Space
`O(n)`. In the worst case, all `k` substrings of `s` are distinct. The hash map must store these `k` keys, each of length `n/k`. The total space for the keys is `k * (n/k) = n`.
Trade-offs
Pros
Optimal time complexity of
O(n), which is faster than the sorting-based approach.Can terminate early as soon as a mismatch is found, potentially saving computation on large inputs.
Cons
While asymptotically faster, hash map operations can have higher constant-factor overheads than array/list operations.
Still requires
O(n)space in the worst-case scenario where all substrings are unique.
Solutions
Solution
class Solution {public boolean isPossibleToRearrange(String s, String t, int k) { Map<String, Integer> cnt = new HashMap<>(k); int n = s.length(); int m = n / k; for (int i = 0; i < n; i += m) { cnt.merge(s.substring(i, i + m), 1, Integer : : sum); cnt.merge(t.substring(i, i + m), -1, Integer : : sum); } for (int v : cnt.values()) { if (v != 0) { return false; } } return true; }}class Solution {public: bool isPossibleToRearrange(string s, string t, int k) { unordered_map<string, int> cnt; int n = s.size(); int m = n / k; for (int i = 0; i < n; i += m) { cnt[s.substr(i, m)]++; cnt[t.substr(i, m)]--; } for (auto &[_, v] : cnt) { if (v) { return false; } } return true; }};class Solution: def isPossibleToRearrange(self, s: str, t: str, k: int) -> bool: cnt = Counter() n = len(s) m = n // k for i in range(0, n, m): cnt[s[i: i + m]] += 1 cnt[t[i: i + m]] -= 1 return all(v == 0 for v in cnt . values())Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.