题目描述

给定中英文混排字符串 s,在每个汉字前后加入 #,相邻汉字之间只保留一个新增分隔符。

原有字符(含原有 #)保持不变;汉字按 Unicode Han 字符判断。

示例 1:

输入: s = "hello你好abc中国"
输出: "hello#你#好#abc#中#国#"
解释: 每个汉字两侧有分隔符,相邻汉字之间共用一个 #;英文内容保持不变。

提示:

  • 按 Unicode Han 字符判断汉字。
  • 保留所有原有字符,包括原有 #。
  • 相邻汉字共用一个新增分隔符。

题意分析

相邻汉字之间只需要一个新增分隔符,所以不能对每个汉字独立替换成 #字# 后直接拼接。另一方面,原有 # 是需要保留的文本,不能把所有连续井号统一压缩。

解法:按汉字状态共享相邻分隔符

核心思路

[!blue]

previousHan 记录上一个原始码点是否为汉字。遇到汉字且前一个不是汉字时,先补前置 #;若前一个也是汉字,其后置分隔符已经写入,本次不再补前置符号。

每个原始码点都原样输出,汉字输出后再追加一个后置 #。随后把 previousHan 更新为当前原码点类型,而不是根据输出末尾字符推断,避免将原有井号与新增分隔符混淆。

Unicode Han 判断按码点进行,非汉字不触发额外分隔。原文中的井号即使与新增井号相邻也保留,只有相邻汉字之间共享新增的那个符号。

解题步骤

  1. 按码点遍历,并记录上一个码点是否为汉字。
  2. 当前为汉字且前一个不是汉字时补前置 #,再输出当前码点。
  3. 汉字后补 #;非汉字原样保留,并更新前一个码点的类型。

代码实现

class Solution {
    public String splitHan(String s) {
        StringBuilder out = new StringBuilder();
        boolean previousHan = false;

        for (int c : s.codePoints().toArray()) {
            boolean han = Character.UnicodeScript.of(c) == Character.UnicodeScript.HAN;

            if (han && !previousHan) {
                out.append('#');
            }

            out.appendCodePoint(c);

            if (han) {
                out.append('#');
            }

            previousHan = han;
        }

        return out.toString();
    }
}
import "unicode"
import "strings"

func splitHan(s string) string {
    var out strings.Builder
    previousHan := false
    for _, c := range s {
        han := unicode.Is(unicode.Han, c)
        if han && !previousHan {
            out.WriteByte('#')
        }
        out.WriteRune(c)
        if han {
            out.WriteByte('#')
        }
        previousHan = han
    }
    return out.String()
}

复杂度分析

  • 时间复杂度:$O(n)$。
  • 空间复杂度:结果与Java码点数组占 $O(n)$ 空间。

关键点总结

[!green]

相邻汉字共享前一个新增的后置分隔符;原文中的 # 是原有内容,不参与删除或压缩。

易错点总结

[!yellow]

按码点处理汉字;原文若已有#,这里保留它,所以可能与新分隔符相邻,不擅自删除输入字符。

转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/53229783
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!