3138. Minimum Length of Anagram Concatenation
Problem Description
You are given a string s that is formed by concatenating multiple anagrams of some unknown string t. Your task is to find the minimum possible length of string t.
An anagram is a rearrangement of the letters of a string. For example, "aab", "aba", and "baa" are all anagrams of each other.
The key insight is that string s is composed entirely of anagrams of t placed one after another. For instance:
- If t = "ab", thenscould be "abba" (concatenation of "ab" and "ba", both anagrams of "ab")
- If t = "abc", thenscould be "abcbcacab" (concatenation of "abc", "bca", and "cab")
Since s is made up of complete anagrams of t, the length of t must be a divisor of the length of s. The algorithm checks each possible divisor k of the length of s, starting from the smallest, to see if s can be partitioned into segments of length k where each segment is an anagram of the others.
The checking process works by:
- Counting the frequency of each character in the entire string s
- For each potential length k, verifying that whensis divided into segments of lengthk, each segment contains the same characters with the same frequencies
- This is validated by ensuring that the character count in any segment, when multiplied by the number of segments (n/k), equals the total character count ins
The function returns the smallest valid length k that satisfies these conditions.
Intuition
The key observation is that if string s is formed by concatenating anagrams of string t, then s must be divisible into equal-length segments where each segment is an anagram of t. This means the length of t must divide the length of s evenly.
Let's think about what this means mathematically. If s has length n and is made up of anagrams of t with length k, then we must have n = m * k for some integer m (the number of anagrams concatenated). This tells us that k must be a divisor of n.
Now, how do we verify if a particular length k is valid? If we split s into segments of length k, each segment should be an anagram of the others. Anagrams have the same character frequencies, so each segment should have identical character counts.
Here's the clever part: instead of comparing all segments pairwise, we can use a global property. If each segment has the same character frequencies, then the total count of any character c in the entire string s should equal the count of c in one segment multiplied by the number of segments. In other words, if character c appears x times in one segment and there are n/k segments, then c should appear exactly x * (n/k) times in the entire string s.
Since we want the minimum possible length, we enumerate divisors of n from smallest to largest. The first divisor that passes our validation check must be the answer, because any smaller valid length would have been found earlier in our enumeration.
This approach is efficient because we only need to count characters once for the entire string, and then for each potential length k, we just need to verify the character counts in the segments match our expected pattern.
Solution Approach
The implementation follows a systematic enumeration and validation approach:
Step 1: Character Frequency Counting
First, we count the frequency of each character in the entire string s using a Counter (hash table), storing it in cnt. This gives us the total occurrence of each character in the string.
Step 2: Enumerate Possible Lengths
We iterate through all possible lengths i from 1 to n (where n = len(s)). For each i, we check if it's a divisor of n by verifying n % i == 0. Only divisors are valid candidates for the length of string t.
Step 3: Validation Function check(k)
For each valid divisor k, we validate whether s can be split into anagrams of length k:
- We iterate through sin chunks of sizekusingrange(0, n, k)
- For each chunk starting at position i, we extract the substrings[i : i + k]
- We count the character frequencies in this chunk using Counter(s[i : i + k]), storing it incnt1
- We then verify that for every character cin the originalcnt:- The equation cnt1[c] * (n // k) == cnt[c]must hold
- This means: (count of cin one segment) × (number of segments) = (total count ofcins)
 
- The equation 
- If any character fails this check, we return False
- If all chunks pass the validation, we return True
Step 4: Return the Minimum Length
Since we enumerate lengths from smallest to largest, the first length i that passes the validation check is immediately returned as the answer. This guarantees we find the minimum possible length.
Time Complexity Analysis:
- Counting characters in s:O(n)
- For each divisor k, thecheckfunction examinesn/ksegments, each of lengthk, resulting inO(n)time
- The number of divisors of nis at mostO(√n)
- Overall complexity: O(n√n)
Space Complexity: O(1) or O(26) for the character counter, since we're dealing with lowercase English letters.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with s = "abcabcbb".
Step 1: Count character frequencies in the entire string
- cnt = {'a': 2, 'b': 4, 'c': 2}
- Length n = 8
Step 2: Try each divisor of 8 from smallest to largest
Try length 1:
- Check if 8 % 1 == 0? Yes, it's a divisor
- Split into 8 segments: ['a'], ['b'], ['c'], ['a'], ['b'], ['c'], ['b'], ['b']
- Take first segment ['a']: cnt1 = {'a': 1}
- Validate: Does cnt1['a'] * 8 = 1 * 8 = 8equalcnt['a'] = 2? No! (8 ≠ 2)
- Length 1 fails ❌
Try length 2:
- Check if 8 % 2 == 0? Yes, it's a divisor
- Split into 4 segments: ['ab'], ['ca'], ['bc'], ['bb']
- Take first segment ['ab']: cnt1 = {'a': 1, 'b': 1}
- Validate:
- Does cnt1['a'] * 4 = 1 * 4 = 4equalcnt['a'] = 2? No! (4 ≠ 2)
 
- Does 
- Length 2 fails ❌
Try length 4:
- Check if 8 % 4 == 0? Yes, it's a divisor
- Split into 2 segments: ['abca'], ['bcbb']
- Take first segment ['abca']: cnt1 = {'a': 2, 'b': 1, 'c': 1}
- Validate:
- Does cnt1['a'] * 2 = 2 * 2 = 4equalcnt['a'] = 2? No! (4 ≠ 2)
 
- Does 
- Length 4 fails ❌
Try length 8:
- Check if 8 % 8 == 0? Yes, it's a divisor
- Split into 1 segment: ['abcabcbb']
- Take first segment ['abcabcbb']: cnt1 = {'a': 2, 'b': 4, 'c': 2}
- Validate:
- Does cnt1['a'] * 1 = 2 * 1 = 2equalcnt['a'] = 2? Yes! ✓
- Does cnt1['b'] * 1 = 4 * 1 = 4equalcnt['b'] = 4? Yes! ✓
- Does cnt1['c'] * 1 = 2 * 1 = 2equalcnt['c'] = 2? Yes! ✓
 
- Does 
- Length 8 passes! ✓
Answer: 8
In this case, the string cannot be formed by concatenating anagrams of any smaller string, so the minimum length is the entire string itself.
Solution Implementation
1from collections import Counter
2
3class Solution:
4    def minAnagramLength(self, s: str) -> int:
5        """
6        Find the minimum length of string t such that s can be formed by 
7        concatenating anagrams of t.
8      
9        Args:
10            s: Input string to analyze
11          
12        Returns:
13            Minimum length of the base string t
14        """
15        def is_valid_length(substring_length: int) -> bool:
16            """
17            Check if the string can be divided into equal parts where each part
18            is an anagram of the same base string.
19          
20            Args:
21                substring_length: Length of each potential substring
22              
23            Returns:
24                True if valid division exists, False otherwise
25            """
26            # Check each substring of the given length
27            for start_idx in range(0, total_length, substring_length):
28                # Get character frequency for current substring
29                current_substring_freq = Counter(s[start_idx : start_idx + substring_length])
30              
31                # Verify that each character appears proportionally
32                # across all substrings
33                for char, total_count in overall_char_freq.items():
34                    expected_count_per_substring = current_substring_freq[char]
35                    num_substrings = total_length // substring_length
36                  
37                    # Check if total count matches expected distribution
38                    if expected_count_per_substring * num_substrings != total_count:
39                        return False
40          
41            return True
42      
43        # Count frequency of all characters in the string
44        overall_char_freq = Counter(s)
45        total_length = len(s)
46      
47        # Try each possible substring length from smallest to largest
48        for candidate_length in range(1, total_length + 1):
49            # Only check lengths that evenly divide the total length
50            if total_length % candidate_length == 0:
51                if is_valid_length(candidate_length):
52                    return candidate_length
531class Solution {
2    private int stringLength;
3    private char[] charArray;
4    private int[] globalCharFrequency = new int[26];
5
6    public int minAnagramLength(String s) {
7        // Initialize instance variables
8        stringLength = s.length();
9        this.charArray = s.toCharArray();
10      
11        // Count frequency of each character in the entire string
12        for (int i = 0; i < stringLength; i++) {
13            globalCharFrequency[charArray[i] - 'a']++;
14        }
15      
16        // Try each possible substring length starting from 1
17        for (int substringLength = 1; ; substringLength++) {
18            // Only check lengths that evenly divide the total string length
19            if (stringLength % substringLength == 0 && isValidSubstringLength(substringLength)) {
20                return substringLength;
21            }
22        }
23    }
24
25    private boolean isValidSubstringLength(int substringLength) {
26        // Check each substring of the given length
27        for (int startIndex = 0; startIndex < stringLength; startIndex += substringLength) {
28            int[] localCharFrequency = new int[26];
29          
30            // Count character frequencies in current substring
31            for (int currentIndex = startIndex; currentIndex < startIndex + substringLength; currentIndex++) {
32                localCharFrequency[charArray[currentIndex] - 'a']++;
33            }
34          
35            // Verify that each character appears proportionally
36            // Each substring should have the same character distribution
37            int numberOfSubstrings = stringLength / substringLength;
38            for (int charIndex = 0; charIndex < 26; charIndex++) {
39                // Total occurrences should equal local occurrences * number of substrings
40                if (localCharFrequency[charIndex] * numberOfSubstrings != globalCharFrequency[charIndex]) {
41                    return false;
42                }
43            }
44        }
45        return true;
46    }
47}
481class Solution {
2public:
3    int minAnagramLength(string s) {
4        int n = s.size();
5      
6        // Count frequency of each character in the entire string
7        int totalFreq[26] = {0};
8        for (char c : s) {
9            totalFreq[c - 'a']++;
10        }
11      
12        // Lambda function to check if string can be divided into anagram substrings of length k
13        auto canDivideIntoAnagrams = [&](int substringLength) {
14            // Check each substring of length k
15            for (int startIdx = 0; startIdx < n; startIdx += substringLength) {
16                // Count frequency of characters in current substring
17                int substringFreq[26] = {0};
18                for (int j = startIdx; j < startIdx + substringLength; ++j) {
19                    substringFreq[s[j] - 'a']++;
20                }
21              
22                // Verify if current substring frequency scaled up matches total frequency
23                // Each character's frequency in substring * number of substrings should equal total frequency
24                int numSubstrings = n / substringLength;
25                for (int charIdx = 0; charIdx < 26; ++charIdx) {
26                    if (substringFreq[charIdx] * numSubstrings != totalFreq[charIdx]) {
27                        return false;
28                    }
29                }
30            }
31            return true;
32        };
33      
34        // Try each possible substring length starting from 1
35        // Return the first valid length (which will be the minimum)
36        for (int length = 1; ; ++length) {
37            // Only check lengths that evenly divide the string length
38            if (n % length == 0 && canDivideIntoAnagrams(length)) {
39                return length;
40            }
41        }
42    }
43};
441/**
2 * Finds the minimum length of an anagram that can be repeated to form the given string
3 * @param s - The input string to analyze
4 * @returns The minimum length of the repeating anagram pattern
5 */
6function minAnagramLength(s: string): number {
7    const stringLength: number = s.length;
8  
9    // Count frequency of each character in the entire string
10    const characterFrequency: Record<string, number> = {};
11    for (let i = 0; i < stringLength; i++) {
12        const char: string = s[i];
13        characterFrequency[char] = (characterFrequency[char] || 0) + 1;
14    }
15  
16    /**
17     * Checks if the string can be divided into segments of length k
18     * where each segment is an anagram of the others
19     * @param segmentLength - The length of each segment to test
20     * @returns True if all segments are anagrams of each other
21     */
22    const isValidSegmentLength = (segmentLength: number): boolean => {
23        const numberOfSegments: number = Math.floor(stringLength / segmentLength);
24      
25        // Check each segment of length segmentLength
26        for (let startIndex = 0; startIndex < stringLength; startIndex += segmentLength) {
27            // Count character frequency in current segment
28            const segmentFrequency: Record<string, number> = {};
29            for (let j = startIndex; j < startIndex + segmentLength; j++) {
30                const char: string = s[j];
31                segmentFrequency[char] = (segmentFrequency[char] || 0) + 1;
32            }
33          
34            // Verify that each character appears the correct number of times
35            // Each character's total count should equal its segment count * number of segments
36            for (const [character, totalCount] of Object.entries(characterFrequency)) {
37                const expectedSegmentCount: number = segmentFrequency[character] || 0;
38                if (expectedSegmentCount * numberOfSegments !== totalCount) {
39                    return false;
40                }
41            }
42        }
43        return true;
44    };
45  
46    // Try each possible segment length starting from 1
47    for (let candidateLength = 1; ; candidateLength++) {
48        // Only test lengths that evenly divide the string length
49        if (stringLength % candidateLength === 0 && isValidSegmentLength(candidateLength)) {
50            return candidateLength;
51        }
52    }
53}
54Time and Space Complexity
Time Complexity: O(n × A), where n is the length of string s, and A is the number of factors (divisors) of n.
The analysis breaks down as follows:
- The outer loop iterates through all possible lengths from 1ton, but only performs the check function whenn % i == 0, meaningiis a divisor ofn. The number of divisors ofnisA.
- For each valid divisor i, thecheckfunction is called:- The check function iterates through the string in chunks of size i, creatingn/isegments
- For the first segment, it creates a Counter which takes O(i)time
- Then it iterates through all unique characters in the original Counter (at most 26for lowercase letters), performing constant-time operations
- Overall, the check function takes O(i + |Σ|)time, which simplifies toO(i)sinceican be up ton
 
- The check function iterates through the string in chunks of size 
- Summing across all divisors: Σ(i for all divisors i of n) ≤ n × A
- Creating the initial Counter takes O(n)time
Therefore, the total time complexity is O(n × A).
Space Complexity: O(|Σ|), where Σ is the character set (lowercase letters in this case).
The space analysis:
- The Counter cntstores at most26character-frequency pairs for lowercase letters:O(|Σ|)
- The Counter cnt1inside the check function also stores at most26entries:O(|Σ|)
- Other variables use constant space
The total space complexity is O(|Σ|), which equals O(26) = O(1) for lowercase English letters.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Redundant Validation of All Segments
The Pitfall:
The current implementation checks every segment of length k in the string, even though mathematically, if all segments are anagrams of each other, they must all have the same character distribution. This means we're doing unnecessary work by validating each segment individually.
Why It Happens:
The code iterates through all segments (for start_idx in range(0, total_length, substring_length)) and validates the character count for each one. However, if the string is valid for a given length k, all segments must be identical in character composition.
The Fix: Instead of checking all segments, we only need to:
- Check if the total character counts are divisible by the number of segments
- Optionally verify that the first segment's character distribution matches the expected pattern
Improved Solution:
from collections import Counter
class Solution:
    def minAnagramLength(self, s: str) -> int:
        def is_valid_length(substring_length: int) -> bool:
            num_segments = total_length // substring_length
          
            # Get the character frequency of the first segment as reference
            first_segment_freq = Counter(s[:substring_length])
          
            # Check if total counts are consistent with this pattern
            for char, count in first_segment_freq.items():
                if overall_char_freq[char] != count * num_segments:
                    return False
          
            # Also check that no extra characters exist in the total count
            for char in overall_char_freq:
                if char not in first_segment_freq:
                    return False
                  
            return True
      
        overall_char_freq = Counter(s)
        total_length = len(s)
      
        for candidate_length in range(1, total_length + 1):
            if total_length % candidate_length == 0:
                if is_valid_length(candidate_length):
                    return candidate_length
2. Not Handling Character Distribution Divisibility Early
The Pitfall: The solution doesn't pre-check whether the character frequencies are compatible with a given segment count before doing detailed validation.
Why It Matters: If we have 7 'a's in the string and we're checking if the string can be divided into 3 segments, it's impossible since 7 is not divisible by 3. We can eliminate such cases immediately.
Optimized Approach:
def is_valid_length(substring_length: int) -> bool:
    num_segments = total_length // substring_length
  
    # Early check: all character counts must be divisible by num_segments
    for char, count in overall_char_freq.items():
        if count % num_segments != 0:
            return False
  
    # Now verify the first segment matches expected pattern
    first_segment_freq = Counter(s[:substring_length])
    for char, count in first_segment_freq.items():
        if overall_char_freq[char] != count * num_segments:
            return False
          
    return True
This optimization can significantly reduce computation time, especially for longer strings with many potential divisors.
Which of the following uses divide and conquer strategy?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!