Java 新 ID 推荐:规范化顺序与截断
ID 规范化依赖转换顺序。尤其是在截断到 15 个字符后,必须再次删除结尾的点。提前删除原有的结尾点,并不能阻止截断暴露出另一个结尾点。
题目规则依次规范化 ASCII 字母大小写、保留允许的字符,再处理点、空结果和长度。两种解法都假设输入满足题目保证的非 null 条件。Optional 解法使用 Java 11 的 repeat(),两种解法均可按 Java 11 编译。任选一种保存为 Solution.java,不要把两个类放入同一文件。
处理流程
流程图对应 Optional 版本。循环版本合并过滤与连续点压缩操作,同时保留相同规则。
用 Optional 连接各步骤
import java.util.Locale;
import java.util.Optional;
public class Solution {
public String solution(String new_id) {
return Optional.of(new_id)
.map(s -> s.toLowerCase(Locale.ROOT))
.map(s -> s.replaceAll("[^a-z0-9._-]", ""))
.map(s -> s.replaceAll("[.]+", "."))
.map(s -> s.replaceAll("^[.]|[.]$", ""))
.map(s -> s.isEmpty() ? "a" : s)
.map(s -> s.length() > 15 ? s.substring(0, 15) : s)
.map(s -> s.replaceAll("[.]$", ""))
.map(s -> s.length() < 3
? s + s.substring(s.length() - 1).repeat(3 - s.length()) : s)
.orElseThrow();
}
}
Locale.ROOT 避免小写转换依赖默认区域设置。允许字符的模式只保留小写 ASCII 字母、数字、点、下划线和连字符。合并连续点并删除两端的点。将空结果替换为 a,截断到 15 个字符,删除截断后暴露的结尾点,再重复最后一个字符,直到长度达到 3。
Optional 用于连接转换步骤,并非必需的数据结构。Optional.of 会拒绝 null,而不会默默将其当作空 ID。
用循环应用相同规则
该解法在一次遍历中合并允许字符过滤和连续点删除。只转换 ASCII 大写字母是基于题目的输入范围,并非通用 Unicode 名称规范化。
import java.util.Objects;
public class Solution {
public String solution(String new_id) {
Objects.requireNonNull(new_id);
StringBuilder result = new StringBuilder();
for (char input : new_id.toCharArray()) {
char c = input >= 'A' && input <= 'Z' ? (char) (input + 32) : input;
boolean allowed = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
|| c == '-' || c == '_' || c == '.';
if (!allowed || (c == '.' && (result.length() == 0
|| result.charAt(result.length() - 1) == '.'))) continue;
result.append(c);
}
if (result.length() > 0 && result.charAt(result.length() - 1) == '.') {
result.setLength(result.length() - 1);
}
if (result.length() == 0) result.append('a');
if (result.length() > 15) result.setLength(15);
if (result.charAt(result.length() - 1) == '.') result.setLength(result.length() - 1);
while (result.length() < 3) result.append(result.charAt(result.length() - 1));
return result.toString();
}
}
| 输入 | 结果 | 检查的规则 |
|---|---|---|
=.= |
aaa |
空结果回退与补齐 |
z-+.^. |
z-- |
允许字符与结尾点删除 |
I |
iii |
不依赖默认区域设置的小写转换 |
abcdefghijklmn.op |
abcdefghijklmn |
删除截断后暴露的点 |
两种解法相对于输入长度 n 的时间复杂度均为 O(n),额外空间为 O(n)。正则版本可能分配多个中间字符串,循环版本则使用 StringBuilder。在没有测量数据时,不能由此断定其中一种总是更快。替换、小写转换和重复的行为可参阅 String API。
数字单词的字面量替换可参阅 replace 与 replaceAll。不依赖区域设置的缓存键可参阅 LRU 缓存示例。
讨论