
本文详解“balanced words counter”面试题核心——明确“subword”即连续子字符串,通过遍历所有非空连续子串并验证其字符频次是否完全相等,最终统计满足条件的数量。关键在于正确建模“平衡”定义与子串枚举逻辑。
在该题目中,“subword”并非任意字符组合或子序列,而是指原字符串中任意一段连续的、非空的子字符串(contiguous substring)。例如,对输入 "aabbabcccba",所有可能的 subword 共有 n × (n+1) / 2 = 11 × 12 / 2 = 66 个(长度从 1 到 11),但其中仅满足“每个出现字母频次严格相等”的才被计入。
所谓“平衡(balanced)”,其数学定义是:设子串中实际出现的字符集合为 S(S ≠ ∅),则对任意 c₁, c₂ ∈ S,均有 count(c₁) == count(c₂)。注意:
- 空串不合法(题目明确要求 balanced word is not empty);
- 只含单个字符的子串(如 "a"、"bb" 中的 "b")天然平衡(因 S 只有一个元素,无比较对象,条件 vacuously true);
- 含多个不同字符时,必须全部频次一致(如 "aabb" ✅,"aab" ❌,"abcabc" ✅,"aabbccca" ❌)。
以 "aabbabcccba"(长度 11)为例,手动枚举所有 66 个连续子串并逐一检验后,恰好有 28 个满足平衡条件。例如:
- 长度 1:"a","a","b","b","a","b","c","c","c","b","a" → 全部 11 个 ✔️
- 长度 2:"aa","ab","bb","ba","ab","bc","cc","cc","cb","ba" → "aa"(2×a)、"bb"(2×b)、"cc"(2×c)、"cc"(2×c)共 4 个 ✔️;其余如 "ab"(1a1b)也 ✔️(因 a 和 b 各出现 1 次)→ 实际共 10 个子串,其中 "ab"、"ba"、"bc"、"cb"、"ab" 均为 1:1,加上 "aa"/"bb"/"cc"/"cc",总计 9 个 ✔️(注:此处需完整校验,下同)
- 更长子串如 "abba"(2a2b)、"aabb"(2a2b)、"abcabc"(但原串无此连续段)、"bcccb"(1b3c1b → 2b3c ❌)等需逐个判断。
✅ 正确实现要点: 输入校验:null 或含非字母字符 → 抛 IllegalArgumentException(题目要求 RuntimeException,建议用 throw new IllegalArgumentException(...)); 枚举策略:双层循环 i=0 to n-1, j=i to n-1,提取 input.substring(i, j+1); 平衡判定:用 Map 统计频次 → 提取所有值 → 使用 Set 去重 → 若 set.size() == 1 且 set.contains(0)==false,则平衡; 边界注意:单字符子串一定平衡;空子串不参与(j>=i 已保证非空)。
以下是核心判定逻辑的简洁示例(可放入私有辅助方法):
立即学习“Java免费学习笔记(深入)”;
private boolean isBalanced(String s) {
if (s.isEmpty()) return false;
Map freq = new HashMap<>();
for (char c : s.toCharArray()) {
freq.put(c, freq.getOrDefault(c, 0) + 1);
}
Set counts = new HashSet<>(freq.values());
return counts.size() == 1; // 所有出现字符频次相同
} 最后,测试用例验证:
- "" → 0 个子串 → 返回 0;
- "abababa1" → 遇 '1' 立即抛异常;
- null → 首行判空抛异常;
- "aabbabcccba" → 枚举 66 子串,28 个通过 isBalanced() → 返回 28。
总结:本题本质是连续子串枚举 + 频次一致性判定,难点不在算法复杂度(O(n²×k),k 为子串平均长度),而在于精准理解“subword = contiguous substring”及“balanced = non-empty & all present chars appear equally often”。厘清定义,编码便水到渠成。










