一.算法题干
给你一条个人信息 string S
,它可能是一个邮箱地址,也可能是一个电话号码。
我们将隐藏它的隐私信息,通过如下规则:
- 电子邮箱
定义名称name
是长度大于等于 2 (length ≥ 2),并且只包含小写字母a-z
和大写字母A-Z
的字符串。
电子邮箱地址由名称name
开头,紧接着是符号@
,后面接着一个名称name
,再接着一个点号.
,然后是一个名称name
。
电子邮箱地址确定为有效的,并且格式是"[email protected]"
。
为了隐藏电子邮箱,所有的名称name
必须被转换成小写的,并且第一个名称name
的第一个字母和最后一个字母的中间的所有字母由 5 个*
代替。 - 电话号码
电话号码是一串包括数字0-9
,以及{'+', '-', '(', ')', ' '}
这几个字符的字符串。你可以假设电话号码包含 10 到 13 个数字。
电话号码的最后 10 个数字组成本地号码,在这之前的数字组成国际号码。注意,国际号码是可选的。我们只暴露最后 4 个数字并隐藏所有其他数字。
本地号码是有格式的,并且如***-***-1111
这样显示,这里的 1 表示暴露的数字。
为了隐藏有国际号码的电话号码,像+111 111 111 1111
,我们以+***-***-***-1111
的格式来显示。在本地号码前面的+
号和第一个-
号仅当电话号码中包含国际号码时存在。例如,一个 12 位的电话号码应当以+-
开头进行显示。
注意:像(
,)
," "这样的不相干的字符以及不符合上述格式的额外的减号或者加号都应当被删除。
最后,将提供的信息正确隐藏后返回。
二.解题思路
这道题属于特殊情况比较多、但是不涉及高级数据结构与算法的题目。我的做法是先根据原字符串中是否有@
字符来判断该字符串属于邮件还是电话号码,然后再分情况分别进行处理。对于邮箱而言,从@
往后(包括@
)的所有字符都要保留,当中如果有大写字母需要转换为小写字母,对于@
之前的字符,仅保留第一个和最后一个,再补5个*
即可完成;对于电话号码而言,需要从后往前统计数字出现的个数,保留最先出现的4个,再补上必有的部分,然后根据数字总数来判定是否有可选的国际号码,如果有的话,国际号码总共有多少位,填上对应数量的*
,最后补上+
。
这道题还有一个特点是:该算法属于真实web工程项目当中较为常见的算法,因为工程当中的算法主要是为特定业务场景服务的,在本题目中的特定业务场景即为在给用户显示信息时隐藏个人信息、仅暴露局部信息。
三.实现代码
string maskPII(string S) {
string res="";
int pos=S.find("@");
if(pos!=string::npos)
{
res=S.substr(pos);
for(int i=1;i<res.length();++i)
if(res[i]>='A'&&res<='Z') res[i]+=32;
char tc=S[0],tc1=S[pos-1];
if(S[0]>='A'&&S[0]<='Z') tc=S[0]+32;
if(S[pos-1]>='A'&&S[pos-1]<='Z') tc1=S[pos-1]+32;
res=tc1+res;
res="*****"+res;
res=tc+res;
}
else
{
int num=0,i,j;
for(i=S.length()-1;;--i)
{
if(S[i]>='0'&&S[i]<='9')
{
res=S[i]+res;
++num;
if(num==4) break;
}
}
res="***-***-"+res;
for(j=i-1;j<=0;--j)
if(S[j]>='0'&&S[j]<='9') ++num;
if(num!=10)
{
res="-"+res;
for(int i=0;i<num-10;++i) res="*"+res;
res="+"+res;
}
}
return res;
}
四.对比分析
public String maskPII(String S) {
if(S.indexOf("@") >= 0){
// mail
S = S.toLowerCase();
int x = S.indexOf("@");
return "" + S.charAt(0) + "*****" + S.substring(x-1);
}else{
// phone
String last = "";
int digit = 0;
for(int i = S.length()-1;i >= 0;i--){
if(S.charAt(i) >= '0' && S.charAt(i) <= '9'){
if(last.length() < 4){
last = S.charAt(i) + last;
}
digit++;
}
}
if(digit == 10){
return "***-***-" + last;
}else{
String ret = "+";
for(int i = 0;i < digit-10;i++)ret += "*";
return ret + "-***-***-" + last;
}
}
}
该代码实现值得我学习借鉴的地方有:
- 使用了Java中String类的API,直接将字符串先全部转为小写,减少代码量,且无需记住大写字母和小写字母的ASCII码值的大小关系。
- 直接利用Java中String类charAt()方法得到的对象可以和String类对象拼接的方法,一步直接完成拼接操作。