
本文介绍一种高效、可扩展的方法,将字符串中某字符的每次出现按从左到右顺序替换为其累计出现次数(如第1次→'1',第2次→'2'),支持任意长度计数(如'10'、'100'),避免因字符串动态拼接导致的索引偏移问题。
在实际开发中,常需对字符串中特定字符进行“序号化标记”——即首次出现替换为 '1',第二次为 '2',依此类推。例如:
- 输入 "helololol",目标字符 'l' → 输出 "he1o2o3o4"
- 输入 "helololol",目标字符 'o' → 输出 "hel1l2l3l"
⚠️ 注意:不能直接遍历原字符串并用 substring 拼接修改,因为每次替换会改变字符串长度,导致后续 charAt(i) 的索引指向错误位置(如 'l' 被 '10' 替换后,原位置 i 后所有字符右移1位,原 i+1 变成 i+2)。
✅ 推荐方案:构建新字符串(StringBuilder + 一次遍历)
最稳健、清晰且时间复杂度为 O(n) 的方式是——不修改原串,而是逐字符扫描,用 StringBuilder 累积结果,并独立维护计数器:
public static String replaceWithCount(String str, char target) {
if (str == null) return null;
StringBuilder result = new StringBuilder();
int count = 0;
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
if (c == target) {
result.append(++count); // 自增后追加数字(转为字符串自动处理多位数)
} else {
result.append(c);
}
}
return result.toString();
}
// 使用示例
public static void main(String[] args) {
System.out.println(replaceWithCount("helololol", 'l')); // he1o2o3o4
System.out.println(replaceWithCount("helololol", 'o')); // hel1l2l3l
System.out.println(replaceWithCount("hellollololollollol", 'l')); // he12o3l4o5l6o7l8o9l10o11l12o13l14
}? 关键优势解析
- 无索引偏移风险:完全规避 str.substring(0,i) + num + str.substring(i+1) 类操作,不依赖原字符串长度;
- 天然支持大数:count 是 int,result.append(count) 自动将 100 转为 "100"(3字符),无需手动判断位数;
- 空间友好:StringBuilder 内部数组扩容策略高效,平均时间复杂度仍为 O(n);
- 可读性与可维护性高:逻辑线性、无嵌套状态,易于单元测试和扩展(如支持跳过大小写、正则匹配等)。
⚠️ 补充说明与注意事项
- 若需原地修改字符数组(如题目中 char[] arr 场景),可先统计总出现次数,再反向填充(避免前向覆盖影响后续索引),但通常无此必要;
- 不推荐使用 String.replaceFirst() 或正则循环替换,因其内部仍涉及多次扫描与创建新字符串,性能差且难以控制“第几次”的语义;
- 对超长字符串(GB级),可考虑流式处理或内存映射,但本方案在常规业务场景(万级字符内)已足够高效。
综上,StringBuilder + 单次遍历 + 独立计数器 是解决该问题的标准实践,兼顾正确性、性能与工程健壮性。










