1. 题目背景与需求分析
LeetCode 831题"Masking Personal Information"是一道典型的字符串处理题目,主要考察开发者对字符串操作的熟练程度和边界条件的处理能力。题目要求我们对给定的个人信息(可能是电子邮件地址或电话号码)进行脱敏处理,这在现实开发中是非常实用的技能。
这道题在LeetCode题库中被标记为中等难度,但实际解决思路并不复杂。根据我的刷题经验,这类字符串处理题目往往有明确的规则可循,关键在于能否全面考虑各种边界情况。题目给出的两个示例已经暗示了我们需要处理两种完全不同的输入格式:
- 电子邮件格式:如"LeetCode@LeetCode.com"
- 电话号码格式:如"1(234)567-890"
提示:在实际面试中,面试官可能会要求你直接写出处理这两种情况的代码,而不会明确告诉你输入是哪种格式,这就需要我们设计能够自动识别输入类型的逻辑。
2. 电子邮件地址的脱敏处理
2.1 电子邮件格式识别规则
首先我们需要识别输入字符串是否为电子邮件地址。根据题目描述和常识判断,电子邮件地址必须包含"@"符号。因此我们可以用这个特征作为判断条件:
python复制def is_email(s: str) -> bool:
return '@' in s
2.2 电子邮件脱敏算法实现
对于电子邮件地址,题目要求的脱敏规则是:
- 保留第一个字符和最后一个域名前的字符
- 中间的部分用5个"*"代替
- 整个字符串转换为小写
具体实现步骤如下:
python复制def mask_email(email: str) -> str:
# 转换为小写
email = email.lower()
# 分割用户名和域名
name, domain = email.split('@')
# 构建脱敏后的用户名
masked_name = name[0] + '*****' + name[-1]
# 组合返回
return f"{masked_name}@{domain}"
2.3 电子邮件处理的边界情况
在实际编码测试中,我发现几个需要注意的边界情况:
- 用户名部分可能只有2个字符(如"ab@example.com"),这时脱敏结果应该是"a*****b@example.com"
- 输入可能包含大写字母,需要统一转换为小写
- "@"符号前后不能为空
注意:LeetCode的测试用例通常不会包含特别极端的边界情况,但在实际开发中,我们需要考虑更多可能性,比如多个"@"符号的情况、包含特殊字符的情况等。
3. 电话号码的脱敏处理
3.1 电话号码格式识别
如果字符串不是电子邮件地址,我们就默认它是电话号码。电话号码的脱敏处理相对复杂一些,因为:
- 输入可能包含各种分隔符(括号、空格、破折号等)
- 可能有国家区号
- 本地号码的长度可能不同
3.2 电话号码规范化处理
首先我们需要去除所有非数字字符:
python复制def normalize_phone(phone: str) -> str:
return ''.join(c for c in phone if c.isdigit())
3.3 电话号码脱敏算法
根据题目要求,电话号码的脱敏规则是:
- 只显示最后4位数字
- 前面的数字用星号代替
- 如果有国家代码,在开头添加"+***-"前缀
具体实现:
python复制def mask_phone(phone: str) -> str:
digits = normalize_phone(phone)
length = len(digits)
last_four = digits[-4:]
if length == 10: # 无国家代码
return '***-***-' + last_four
else: # 有国家代码
return '+' + '*' * (length - 10) + '-***-***-' + last_four
3.4 电话号码处理的注意事项
在处理电话号码时,有几个关键点需要注意:
- 国际号码的国家代码长度可能不同(1-3位)
- 本地号码固定为10位(美国标准)
- 需要正确处理各种分隔符
- 输入可能全是数字,也可能包含各种格式符号
4. 完整解决方案与性能优化
4.1 主函数逻辑
将上述两部分逻辑组合起来,得到完整的解决方案:
python复制def maskPII(s: str) -> str:
if '@' in s:
return mask_email(s)
else:
return mask_phone(s)
4.2 性能分析与优化
这个解法的时间复杂度是O(n),其中n是输入字符串的长度。空间复杂度也是O(n),主要是由于创建了规范化后的电话号码字符串。
在实际测试中,我发现几个可以优化的点:
- 可以原地操作字符串,减少中间变量的创建
- 对于电话号码处理,可以只遍历一次字符串,边遍历边处理
- 使用字符串的translate方法可能比逐个字符处理更快
优化后的电话号码处理函数:
python复制def mask_phone_optimized(phone: str) -> str:
digits = []
for c in reversed(phone):
if c.isdigit():
digits.append(c)
if len(digits) == 4:
break
last_four = ''.join(reversed(digits))
digit_count = sum(1 for c in phone if c.isdigit())
if digit_count == 10:
return '***-***-' + last_four
else:
return '+' + '*' * (digit_count - 10) + '-***-***-' + last_four
5. 测试用例设计与验证
5.1 电子邮件测试用例
python复制test_cases_email = [
("LeetCode@LeetCode.com", "l*****e@leetcode.com"),
("AB@qq.com", "a*****b@qq.com"),
("a@b.com", "a*****a@b.com"), # 边界情况
("long.username+tag@gmail.com", "l*****e@gmail.com") # 包含特殊字符
]
5.2 电话号码测试用例
python复制test_cases_phone = [
("1(234)567-890", "***-***-7890"),
("86-(10)12345678", "+**-***-***-5678"),
("+1 (234) 567-890", "+*-***-***-7890"),
("1234567890", "***-***-7890"), # 无分隔符
("+111 111 111 1111", "+***-***-***-1111") # 长国家代码
]
5.3 自动化测试实现
为了确保代码的正确性,我通常会编写自动化测试脚本:
python复制def run_tests():
for input_str, expected in test_cases_email + test_cases_phone:
result = maskPII(input_str)
assert result == expected, f"Failed: {input_str} -> {result}, expected {expected}"
print("All tests passed!")
6. 实际应用与扩展思考
6.1 现实中的个人信息脱敏
在实际开发中,个人信息脱敏的需求比这道题目复杂得多。我们可能需要考虑:
- 更多类型的个人信息(身份证号、银行卡号等)
- 不同国家/地区的电话号码格式
- 更复杂的脱敏规则(如保留前几位后几位)
- 性能要求(处理大量数据时的效率)
6.2 更健壮的实现方案
对于生产环境,我建议:
- 使用正则表达式进行更精确的格式验证
- 添加输入合法性检查
- 考虑国际化支持(不同国家的电话号码格式)
- 添加日志记录和监控
一个更健壮的正则表达式实现示例:
python复制import re
def mask_email_regex(email: str) -> str:
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', email):
raise ValueError("Invalid email format")
# 其余处理逻辑...
6.3 类似题目推荐
如果你觉得这道题目有帮助,可以尝试以下类似题目巩固字符串处理技能:
- LeetCode 929. Unique Email Addresses
- LeetCode 1108. Defanging an IP Address
- LeetCode 1456. Maximum Number of Vowels in a Substring of Given Length
- LeetCode 1576. Replace All ?'s to Avoid Consecutive Repeating Characters
7. 刷题心得与技巧分享
通过这道题目,我总结了一些LeetCode刷题的经验:
- 仔细阅读题目:确保完全理解题目要求和边界条件
- 先设计再编码:在写代码前先设计好算法步骤
- 测试驱动开发:先写测试用例,再实现功能
- 考虑优化:在保证正确性的前提下考虑性能优化
- 总结模式:识别题目类型,建立解题模式库
对于字符串处理类题目,我的常用技巧包括:
- 使用字符串的split()、join()、replace()等方法
- 掌握正则表达式的基本用法
- 注意字符串的不可变性,避免不必要的拷贝
- 使用双指针技巧处理复杂的字符串操作
- 考虑使用内置的字符串处理函数(如str.translate)
个人经验:在LeetCode周赛中,字符串处理题目往往出现在前两道题,特点是思路直接但细节多。建议平时多练习这类题目,提高一次性通过率。
