Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Move from brute-force thinking to an efficient approach using hash map strategy.
You are given a string s, which is known to be a concatenation of anagrams of some string t.
Return the minimum possible length of the string t.
An anagram is formed by rearranging the letters of a string. For example, "aab", "aba", and, "baa" are anagrams of "aab".
Example 1:
Input: s = "abba"
Output: 2
Explanation:
One possible string t could be "ba".
Example 2:
Input: s = "cdef"
Output: 4
Explanation:
One possible string t could be "cdef", notice that t can be equal to s.
Example 2:
Input: s = "abcbcacabbaccba"
Output: 3
Constraints:
1 <= s.length <= 105s consist only of lowercase English letters.Problem summary: You are given a string s, which is known to be a concatenation of anagrams of some string t. Return the minimum possible length of the string t. An anagram is formed by rearranging the letters of a string. For example, "aab", "aba", and, "baa" are anagrams of "aab".
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map
"abba"
"cdef"
"abcbcacabbaccba"
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #3138: Minimum Length of Anagram Concatenation
class Solution {
private int n;
private char[] s;
private int[] cnt = new int[26];
public int minAnagramLength(String s) {
n = s.length();
this.s = s.toCharArray();
for (int i = 0; i < n; ++i) {
++cnt[this.s[i] - 'a'];
}
for (int i = 1;; ++i) {
if (n % i == 0 && check(i)) {
return i;
}
}
}
private boolean check(int k) {
for (int i = 0; i < n; i += k) {
int[] cnt1 = new int[26];
for (int j = i; j < i + k; ++j) {
++cnt1[s[j] - 'a'];
}
for (int j = 0; j < 26; ++j) {
if (cnt1[j] * (n / k) != cnt[j]) {
return false;
}
}
}
return true;
}
}
// Accepted solution for LeetCode #3138: Minimum Length of Anagram Concatenation
func minAnagramLength(s string) int {
n := len(s)
cnt := [26]int{}
for _, c := range s {
cnt[c-'a']++
}
check := func(k int) bool {
for i := 0; i < n; i += k {
cnt1 := [26]int{}
for j := i; j < i+k; j++ {
cnt1[s[j]-'a']++
}
for j, v := range cnt {
if cnt1[j]*(n/k) != v {
return false
}
}
}
return true
}
for i := 1; ; i++ {
if n%i == 0 && check(i) {
return i
}
}
}
# Accepted solution for LeetCode #3138: Minimum Length of Anagram Concatenation
class Solution:
def minAnagramLength(self, s: str) -> int:
def check(k: int) -> bool:
for i in range(0, n, k):
cnt1 = Counter(s[i : i + k])
for c, v in cnt.items():
if cnt1[c] * (n // k) != v:
return False
return True
cnt = Counter(s)
n = len(s)
for i in range(1, n + 1):
if n % i == 0 and check(i):
return i
// Accepted solution for LeetCode #3138: Minimum Length of Anagram Concatenation
/**
* [3138] Minimum Length of Anagram Concatenation
*/
pub struct Solution {}
// submission codes start here
use std::collections::HashMap;
impl Solution {
pub fn min_anagram_length(s: String) -> i32 {
let s: Vec<char> = s.chars().collect();
let length = s.len();
for i in 1..length {
if length % i != 0 {
continue;
}
let count = length / i;
let mut standard_map = HashMap::new();
for k in 0..i {
let entry = standard_map.entry(s[k]).or_insert(0);
*entry += 1;
}
let mut flag = true;
for j in 1..count {
let mut map = HashMap::new();
for k in (j * i)..(i * (j + 1)) {
let entry = map.entry(s[k]).or_insert(0);
*entry += 1;
}
flag = map == standard_map;
if !flag {
break;
}
}
if flag {
return i as i32;
}
}
s.len() as i32
}
}
// submission codes end
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_3138() {
assert_eq!(2, Solution::min_anagram_length("abba".to_owned()));
assert_eq!(4, Solution::min_anagram_length("cdef".to_owned()));
}
}
// Accepted solution for LeetCode #3138: Minimum Length of Anagram Concatenation
function minAnagramLength(s: string): number {
const n = s.length;
const cnt: Record<string, number> = {};
for (let i = 0; i < n; i++) {
cnt[s[i]] = (cnt[s[i]] || 0) + 1;
}
const check = (k: number): boolean => {
for (let i = 0; i < n; i += k) {
const cnt1: Record<string, number> = {};
for (let j = i; j < i + k; j++) {
cnt1[s[j]] = (cnt1[s[j]] || 0) + 1;
}
for (const [c, v] of Object.entries(cnt)) {
if (cnt1[c] * ((n / k) | 0) !== v) {
return false;
}
}
}
return true;
};
for (let i = 1; ; ++i) {
if (n % i === 0 && check(i)) {
return i;
}
}
}
Use this to step through a reusable interview workflow for this problem.
For each element, scan the rest of the array looking for a match. Two nested loops give n × (n−1)/2 comparisons = O(n²). No extra space since we only use loop indices.
One pass through the input, performing O(1) hash map lookups and insertions at each step. The hash map may store up to n entries in the worst case. This is the classic space-for-time tradeoff: O(n) extra memory eliminates an inner loop.
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.