
本文介绍如何准确判断一个字符串是否包含另一个字符串中的全部字符(包括重复字符),解决简单遍历导致的重复字符误判问题,提供基于字符列表动态移除的高效解决方案。
在字符串匹配场景中,常需验证 word1 是否“覆盖”word2 的所有字符——即 word1 中每个字符的出现频次均不少于 word2 中对应字符的频次。例如:word1 = "12345++",word2 = "155",期望返回 false(因 word1 仅含一个 '5',而 word2 需要两个),但原始代码使用 String.contains() 会忽略字符数量限制,导致错误通过。
根本问题在于:原始逻辑未做字符消耗管理——每次调用 contains() 都是全局搜索,无法体现“用过即减少”的语义。
✅ 正确解法:动态字符池 + 即时移除
核心思路是将 word1 的所有字符构建成可变集合(如 ArrayList
以下是优化后的完整实现:
import java.util.*;
public static boolean isItemUsable2(String word1, String word2) {
// 边界处理:空字符串逻辑
if (word2 == null || word2.isEmpty()) return true;
if (word1 == null) return false;
// 构建可修改的字符池(允许重复)
List availableChars = new ArrayList<>();
for (char c : word1.toCharArray()) {
availableChars.add(c);
}
// 逐个消耗 word2 中的字符
for (char target : word2.toCharArray()) {
// remove(Object) 返回 boolean:true 表示成功移除一个匹配元素
if (!availableChars.remove((Character) target)) {
return false; // 字符不足,提前终止
}
}
return true;
} ? 关键细节说明
- availableChars.remove((Character) target) 调用的是 List.remove(Object)(而非 remove(int)),它按值查找并移除第一个匹配项,天然支持重复字符的精确计数;
- 强制类型转换 (Character) target 是必要的,否则会误调 remove(int index) 导致 IndexOutOfBoundsException;
- 时间复杂度为 O(m × n)(m、n 分别为两字符串长度),适用于中小规模数据;如需更高性能(如超长字符串),可改用 HashMap
统计频次(O(m + n)); - 该方法区分大小写且包含所有 Unicode 字符,符合 Java 默认字符串行为。
⚠️ 注意事项
- 不要使用 Set(如 HashSet)替代 List:Set 自动去重,无法表达“需要两个 '5'”的语义;
- 避免在循环中反复调用 word1.indexOf() 或正则匹配,同样无法控制字符消耗;
- 若需忽略空格、大小写等业务规则,应在构建 availableChars 和遍历 word2 前统一预处理(如 toLowerCase().replaceAll("\\s+", ""))。
此方案简洁、健壮、易于理解,是解决“子多重集匹配”类问题的经典实践。










