LeetCode 补充题 171. 汉字的井号分隔
题目描述
给定中英文混排字符串
s,在每个汉字前后加入#,相邻汉字之间只保留一个新增分隔符。原有字符(含原有
#)保持不变;汉字按 Unicode Han 字符判断。
示例 1:
输入:
s = "hello你好abc中国"
输出:"hello#你#好#abc#中#国#"
解释: 每个汉字两侧有分隔符,相邻汉字之间共用一个 #;英文内容保持不变。
提示:
- 按 Unicode Han 字符判断汉字。
- 保留所有原有字符,包括原有
#。 - 相邻汉字共用一个新增分隔符。
题意分析
相邻汉字之间只需要一个新增分隔符,所以不能对每个汉字独立替换成
#字#后直接拼接。另一方面,原有#是需要保留的文本,不能把所有连续井号统一压缩。
解法:按汉字状态共享相邻分隔符
核心思路
[!blue]
previousHan记录上一个原始码点是否为汉字。遇到汉字且前一个不是汉字时,先补前置#;若前一个也是汉字,其后置分隔符已经写入,本次不再补前置符号。每个原始码点都原样输出,汉字输出后再追加一个后置
#。随后把previousHan更新为当前原码点类型,而不是根据输出末尾字符推断,避免将原有井号与新增分隔符混淆。Unicode Han 判断按码点进行,非汉字不触发额外分隔。原文中的井号即使与新增井号相邻也保留,只有相邻汉字之间共享新增的那个符号。
解题步骤
- 按码点遍历,并记录上一个码点是否为汉字。
- 当前为汉字且前一个不是汉字时补前置 #,再输出当前码点。
- 汉字后补 #;非汉字原样保留,并更新前一个码点的类型。
代码实现
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]
按码点处理汉字;原文若已有#,这里保留它,所以可能与新分隔符相邻,不擅自删除输入字符。
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!