文章目录
题目
标题和出处
标题:隐藏个人信息
出处:831. 隐藏个人信息
难度
4 级
题目描述
要求
给你一条个人信息字符串 s \texttt{s} s,它可能是一个电子邮箱,也可能是一串电话号码。
我们将通过如下规则隐藏它的隐私信息。
电子邮箱
电子邮箱的格式如下:
- 名称由大写和小写英语字母组成。
- 后面接着符号 ‘@’ \texttt{`@'} ‘@’。
- 后面接着由大写和小写英语字母组成的域名,域名中有一个点号 ‘.’ \texttt{`.'} ‘.’(点号不是第一个或最后一个字符)。
隐藏电子邮箱的做法如下:
- 名称和域名中的大写字母必须转换成小写字母;
- 名称中的中间字母(即除了第一个和最后一个字母以外的全部字母)必须由 5 \texttt{5} 5 个星号 "*****" \texttt{"*****"} "*****" 代替。
电话号码
电话号码的格式如下:
- 电话号码包含 10 \texttt{10} 10 到 13 \texttt{13} 13 个数字。
- 最后 10 \texttt{10} 10 个数字组成本地号码。
- 开头的其余 0 \texttt{0} 0 到 3 \texttt{3} 3 个数字组成国际号码。
- 分隔字符包含 {‘+’, ‘-’, ‘(’, ‘)’, ‘ ’} \texttt{\{`+', `-', `(', `)', ` '\}} {‘+’, ‘-’, ‘(’, ‘)’, ‘ ’},分隔数字。
隐藏电话号码的做法如下:
- 删除所有的分割字符。
- 隐藏后的电话号码的格式如下:
- 国际号码为 0 \texttt{0} 0 个数字时,电话号码为 "***-***-XXXX" \texttt{"***-***-XXXX"} "***-***-XXXX"。
- 国际号码为 1 \texttt{1} 1 个数字时,电话号码为 "+*-***-***-XXXX" \texttt{"+*-***-***-XXXX"} "+*-***-***-XXXX"。
- 国际号码为 2 \texttt{2} 2 个数字时,电话号码为 "+**-***-***-XXXX" \texttt{"+**-***-***-XXXX"} "+**-***-***-XXXX"。
- 国际号码为 3 \texttt{3} 3 个数字时,电话号码为 "+***-***-***-XXXX" \texttt{"+***-***-***-XXXX"} "+***-***-***-XXXX"。
- "XXXX" \texttt{"XXXX"} "XXXX" 是本地号码的最后 4 \texttt{4} 4 个数字。
示例
示例 1:
输入:
s
=
"LeetCode@LeetCode.com"
\texttt{s = "LeetCode@LeetCode.com"}
s = "LeetCode@LeetCode.com"
输出:
"l*****e@leetcode.com"
\texttt{"l*****e@leetcode.com"}
"l*****e@leetcode.com"
解释:
s
\texttt{s}
s 是电子邮箱。
名称和域名转换成小写, 名称的中间字符由
5
\texttt{5}
5 个星号代替。
示例 2:
输入:
s
=
"AB@qq.com"
\texttt{s = "AB@qq.com"}
s = "AB@qq.com"
输出:
"a*****b@qq.com"
\texttt{"a*****b@qq.com"}
"a*****b@qq.com"
解释:
s
\texttt{s}
s 是电子邮箱。
名称和域名转换成小写, 名称的中间字符由
5
\texttt{5}
5 个星号代替。
注意虽然
"ab"
\texttt{"ab"}
"ab" 只有
2
\texttt{2}
2 个字符,中间也必须有
5
\texttt{5}
5 个星号。
示例 3:
输入:
s
=
"1(234)567-890"
\texttt{s = "1(234)567-890"}
s = "1(234)567-890"
输出:
"***-***-7890"
\texttt{"***-***-7890"}
"***-***-7890"
解释:
s
\texttt{s}
s 是电话号码。
有
10
\texttt{10}
10 个数字的电话号码,所以本地号码是
10
\texttt{10}
10 个数字,国际号码是
0
\texttt{0}
0 个数字。
因此隐藏后的电话号码是
"***-***-7890"
\texttt{"***-***-7890"}
"***-***-7890"。
示例 4:
输入:
s
=
"86-(10)12345678"
\texttt{s = "86-(10)12345678"}
s = "86-(10)12345678"
输出:
"+**-***-***-5678"
\texttt{"+**-***-***-5678"}
"+**-***-***-5678"
解释:
s
\texttt{s}
s 是电话号码。
有
12
\texttt{12}
12 个数字的电话号码,所以本地号码是
10
\texttt{10}
10 个数字,国际号码是
2
\texttt{2}
2 个数字。
因此隐藏后的电话号码是
"+**-***-***-5678"
\texttt{"+**-***-***-5678"}
"+**-***-***-5678"。
数据范围
- s \texttt{s} s 是有效的电子邮箱或电话号码
- 如果
s
\texttt{s}
s 是电子邮箱:
- 8 ≤ s.length ≤ 40 \texttt{8} \le \texttt{s.length} \le \texttt{40} 8≤s.length≤40
- s \texttt{s} s 由大小写英语字母、一个 ‘@’ \texttt{`@'} ‘@’ 和一个 ‘.’ \texttt{`.'} ‘.’ 组成
- 如果
s
\texttt{s}
s 是电话号码:
- 10 ≤ s.length ≤ 20 \texttt{10} \le \texttt{s.length} \le \texttt{20} 10≤s.length≤20
- s \texttt{s} s 由数字、空格、 ‘(’ \texttt{`('} ‘(’、 ‘)’ \texttt{`)'} ‘)’、 ‘-’ \texttt{`-'} ‘-’ 和 ‘+’ \texttt{`+'} ‘+’ 组成
解法
思路和算法
对于给定的字符串 s s s,需要首先判断是电子邮箱还是电话号码,然后隐藏个人信息。
由于电子邮箱一定以字母开头,电话号码不包含字母,因此可以通过 s s s 的首个字符判断 s s s 是电子邮箱还是电话号码,如果 s s s 的首个字符是字母,则 s s s 是电子邮箱,否则 s s s 是电话号码。
对于电子邮箱,隐藏个人信息时需要对 ‘@’ \text{`@'} ‘@’ 符号前面的部分只保留第一个字母和最后一个字母,中间用 5 5 5 个星号填充,对 ‘@’ \text{`@'} ‘@’ 符号前面的部分保持原样,然后将隐藏个人信息之后的字符串转成小写。将 ‘@’ \text{`@'} ‘@’ 符号所在下标记为 atIndex \textit{atIndex} atIndex,则隐藏个人信息之后的字符串包括 s s s 的首个字符、 5 5 5 个星号以及 s s s 的下标从 atIndex − 1 \textit{atIndex} - 1 atIndex−1 到末尾的全部字符。由此可以得到如下实现:将 s s s 的首个字符拼接到结果字符串,然后将 s s s 的下标从 atIndex − 1 \textit{atIndex} - 1 atIndex−1 到末尾的全部字符依次拼接到结果字符串,在拼接的过程中,每个字符都需要转成小写之后再拼接,这样才能确保结果字符串中的字母全部是小写字母。
对于电话号码,首先需要遍历字符串 s s s 计算数字的个数,判断是本地号码还是国际号码,然后隐藏电话号码,如果数字的个数大于 10 10 10,则是国际号码,结果字符串的第一个字符应为 ‘+’ \text{`+'} ‘+’。然后第二次遍历字符串 s s s,当遇到非数字字符时跳过,当遇到数字时进行如下操作:
-
如果剩下的数字个数大于 4 4 4,则将一个星号拼接到结果字符串,否则将当前数字拼接到结果字符串;
-
将剩下的数字个数减 1 1 1,如果在更新剩下的数字个数之后,剩下的数字个数是 10 10 10、 7 7 7 或 4 4 4,则将 ‘–’ \text{`--'} ‘–’ 拼接到结果字符串。
代码
class Solution {
public String maskPII(String s) {
if (Character.isLetter(s.charAt(0))) {
return maskEmailAddress(s);
} else {
return maskPhoneNumber(s);
}
}
public String maskEmailAddress(String s) {
int length = s.length();
int atIndex = s.indexOf('@');
StringBuffer sb = new StringBuffer();
sb.append(Character.toLowerCase(s.charAt(0)));
sb.append("*****");
for (int i = atIndex - 1; i < length; i++) {
sb.append(Character.toLowerCase(s.charAt(i)));
}
return sb.toString();
}
public String maskPhoneNumber(String s) {
int count = 0;
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) {
count++;
}
}
StringBuffer sb = new StringBuffer();
if (count > 10) {
sb.append('+');
}
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (Character.isDigit(c)) {
if (count > 4) {
sb.append('*');
} else {
sb.append(c);
}
count--;
if (count == 10 || count == 7 || count == 4) {
sb.append('-');
}
}
}
return sb.toString();
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。最多需要遍历字符串 s s s 两次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要额外创建一个长度为 O ( n ) O(n) O(n) 的 StringBuffer \texttt{StringBuffer} StringBuffer 或 StringBuilder \texttt{StringBuilder} StringBuilder 类型的对象用于存储隐藏个人信息之后的新字符串。由于 Java 中的 String \texttt{String} String 类型的对象不可变,因此空间复杂度至少为 O ( n ) O(n) O(n)。