Medium
题目描述
给你一个个人信息字符串 s,可能表示的是一个邮件地址,也可能表示一根电话号码。请你按照如下规则对个人信息进行屏蔽:
邮件地址:
邮件地址满足以下格式:
- 一个名字,由大小写英文字母组成,后面跟着
- ‘@‘符号,后面跟着
- 一个域名,由大小写英文字母组成,中间有一个点’.’(不是第一个或最后一个字符)
屏蔽邮件的规则:
- 名字和域名的大写字母需要转换成小写字母
- 名字中间的字母(除了第一个和最后一个字母)用5个星号"*****“代替
电话号码:
电话号码的格式如下:
- 电话号码包含10-13位数字
- 最后10位数字组成当地号码
- 开头剩下的0-3位数字组成国家代码
- 分隔符 {’+’, ‘-’, ‘(’, ‘)’, ’ ‘} 将上述数字分开
屏蔽电话号码的规则:
- 移除所有分隔符
- 屏蔽后的电话号码格式为:
- 如果国家代码有0位数字,格式为 “--XXXX”
- 如果国家代码有1位数字,格式为 “+--*-XXXX”
- 如果国家代码有2位数字,格式为 “+--**-XXXX”
- 如果国家代码有3位数字,格式为 “+--***-XXXX”
- “XXXX"是当地号码的最后4位数字
示例 1:
输入:s = "LeetCode@LeetCode.com"
输出:"l*****e@leetcode.com"
解释:s是一个邮件地址。名字和域名转换为小写,名字中间用5个星号代替。
示例 2:
输入:s = "AB@qq.com"
输出:"a*****b@qq.com"
解释:s是一个邮件地址。名字和域名转换为小写,名字中间用5个星号代替。
注意虽然"ab"只有2个字符,仍然需要在中间放5个星号。
示例 3:
输入:s = "1(234)567-890"
输出:"***-***-7890"
解释:s是一个电话号码。有10位数字,所以当地号码是10位,国家代码是0位。
因此屏蔽后的号码是"***-***-7890"。
提示:
- s 要么是一个有效的邮件地址,要么是一个电话号码
- 如果 s 是邮件地址:
- 8 <= s.length <= 40
- s 包含大小写英文字母和恰好一个’@‘符号和’.‘符号
- 如果 s 是电话号码:
- 10 <= s.length <= 20
- s 包含数字、空格和符号’(’, ‘)’, ‘-’, ‘+’
解题思路
这道题需要分别处理邮件地址和电话号码两种情况,通过检测字符串中是否包含’@‘符号来区分。
对于邮件地址:
- 将整个字符串转为小写
- 找到’@‘的位置,分别处理用户名和域名
- 用户名保留第一个和最后一个字符,中间用5个星号替换
对于电话号码:
- 去除所有非数字字符,得到纯数字字符串
- 根据数字总长度(10-13位)确定国家代码长度
- 按照规定格式构造屏蔽后的号码
实现思路清晰,关键在于正确处理边界情况:
- 邮件用户名长度不足2个字符时也要用5个星号
- 电话号码按国家代码长度选择合适的格式模板
代码实现相对直接,主要使用字符串操作和条件判断。
代码实现
class Solution {
public:
string maskPII(string s) {
if (s.find('@') != string::npos) {
// Email
transform(s.begin(), s.end(), s.begin(), ::tolower);
int at = s.find('@');
string name = s.substr(0, at);
string domain = s.substr(at);
return name[0] + string(5, '*') + name.back() + domain;
} else {
// Phone
string digits = "";
for (char c : s) {
if (isdigit(c)) {
digits += c;
}
}
int n = digits.length();
string last4 = digits.substr(n - 4);
if (n == 10) {
return "***-***-" + last4;
} else if (n == 11) {
return "+*-***-***-" + last4;
} else if (n == 12) {
return "+**-***-***-" + last4;
} else {
return "+***-***-***-" + last4;
}
}
}
};
class Solution:
def maskPII(self, s: str) -> str:
if '@' in s:
# Email
s = s.lower()
at_pos = s.index('@')
name = s[:at_pos]
domain = s[at_pos:]
return name[0] + '*' * 5 + name[-1] + domain
else:
# Phone
digits = ''.join(c for c in s if c.isdigit())
n = len(digits)
last4 = digits[-4:]
if n == 10:
return "***-***-" + last4
elif n == 11:
return "+*-***-***-" + last4
elif n == 12:
return "+**-***-***-" + last4
else:
return "+***-***-***-" + last4
public class Solution {
public string MaskPII(string s) {
if (s.Contains('@')) {
// Email
s = s.ToLower();
int atPos = s.IndexOf('@');
string name = s.Substring(0, atPos);
string domain = s.Substring(atPos);
return name[0] + new string('*', 5) + name[name.Length - 1] + domain;
} else {
// Phone
string digits = "";
foreach (char c in s) {
if (char.IsDigit(c)) {
digits += c;
}
}
int n = digits.Length;
string last4 = digits.Substring(n - 4);
if (n == 10) {
return "***-***-" + last4;
} else if (n == 11) {
return "+*-***-***-" + last4;
} else if (n == 12) {
return "+**-***-***-" + last4;
} else {
return "+***-***-***-" + last4;
}
}
}
}
/**
* @param {string} s
* @return {string}
*/
var maskPII = function(s) {
if (s.includes('@')) {
// Email
s = s.toLowerCase();
const atIndex = s.indexOf('@');
const name = s.substring(0, atIndex);
const domain = s.substring(atIndex);
if (name.length <= 2) {
return name[0] + '*****' + name[name.length - 1] + domain;
} else {
return name[0] + '*****' + name[name.length - 1] + domain;
}
} else {
// Phone number
const digits = s.replace(/[^\d]/g, '');
const localDigits = digits.slice(-10);
const countryCodeLength = digits.length - 10;
const lastFour = localDigits.slice(-4);
if (countryCodeLength === 0) {
return '***-***-' + lastFour;
} else {
const stars = '*'.repeat(countryCodeLength);
return '+' + stars + '-***-***-' + lastFour;
}
}
};
复杂度分析
| 复杂度类型 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 总体 | O(n) | O(n) |
| 邮件处理 | O(n) | O(n) |
| 电话处理 | O(n) | O(n) |
其中 n 为输入字符串的长度。时间复杂度主要来自字符串的遍历和处理,空间复杂度来自存储处理结果。