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 包含数字、空格和符号’(’, ‘)’, ‘-’, ‘+’

解题思路

这道题需要分别处理邮件地址和电话号码两种情况,通过检测字符串中是否包含’@‘符号来区分。

对于邮件地址:

  1. 将整个字符串转为小写
  2. 找到’@‘的位置,分别处理用户名和域名
  3. 用户名保留第一个和最后一个字符,中间用5个星号替换

对于电话号码:

  1. 去除所有非数字字符,得到纯数字字符串
  2. 根据数字总长度(10-13位)确定国家代码长度
  3. 按照规定格式构造屏蔽后的号码

实现思路清晰,关键在于正确处理边界情况:

  • 邮件用户名长度不足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 为输入字符串的长度。时间复杂度主要来自字符串的遍历和处理,空间复杂度来自存储处理结果。