leetcode
leetcode 1501 ~ 1550
重新格式化电话号码

重新格式化电话号码

难度:

标签:

题目描述

代码结果

运行时间: 24 ms, 内存: 0.0 MB


/*
 * 思路:
 * 1. 删除所有空格和破折号
 * 2. 按照每3个数字分组,直到剩下4个或更少的数字
 * 3. 如果剩下4个数字,将其分为两个含2个数字的块
 * 4. 用破折号连接这些块
 */

import java.util.stream.Collectors;

public class Solution {
    public String reformatNumber(String number) {
        // Step 1: Remove all spaces and dashes
        String cleaned = number.chars()
                               .filter(Character::isDigit)
                               .mapToObj(c -> String.valueOf((char) c))
                               .collect(Collectors.joining());
        StringBuilder result = new StringBuilder();
        int length = cleaned.length();
        int index = 0;

        // Step 2: Process the main part of the string
        while (length > 4) {
            result.append(cleaned, index, index + 3).append("-");
            index += 3;
            length -= 3;
        }

        // Step 3: Handle the remaining part of the string
        if (length == 4) {
            result.append(cleaned, index, index + 2).append("-")
                  .append(cleaned, index + 2, index + 4);
        } else { // length is 2 or 3
            result.append(cleaned, index, cleaned.length());
        }

        return result.toString();
    }
}

解释

方法:

题解采用了正则表达式进行处理。首先,使用 re.sub('\D', '', number) 删除输入字符串中所有非数字的字符,包括空格和破折号。然后,使用另一个正则表达式 re.sub('(...?(?=..))', r'\1-', ...) 在适当的位置插入破折号。这个正则表达式分为两部分:'...?' 匹配2到3个数字;'(?=..)' 是正向前瞻,确保后面至少还有两个字符,这样可以保证不会在字符串末尾添加破折号。通过这种方式,能够正确地将字符串分组为每三个数字一组,直到最后剩余的数字按题目要求进行特殊处理。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
题解中的正则表达式`(...?(?=..))`是如何确保在数字剩余4个时正确分为两组的?
正则表达式`(...?(?=..))`中的`...?`部分可以匹配2到3个数字。其关键在于正向前瞻`(?=..)`,这保证在当前匹配后至少还有两个数字。当数字总数为4时,首次匹配会捕获前两个数字(因为如果匹配3个,后面就剩一个,不满足前瞻条件),然后剩余两个数字在下一次迭代中被处理。因此,这种方法自然而然地将4个数字分为两组,每组两个。
🦆
在使用正则表达式`re.sub('(...?(?=..))', r'\1-', digits_only)`时,这种方法是否会在某些特殊情况下引入非预期的破折号?例如,如果输入是'123456789',输出会是什么?
在这种情况下,正则表达式`(...?(?=..))`确保只在至少后续有两个数字时才插入破折号。例如输入'123456789',处理过程如下:首先`123`匹配并插入破折号得到`123-`,接着`456`匹配得到`123-456-`,然后`789`匹配,因为之后没有额外数字,所以不再添加破折号,最终输出为`123-456-789`。此方法不会在末尾引入非预期的破折号。
🦆
正则表达式中的前瞻`(?=..)`部分是如何工作的,它如何确保在适当的位置插入破折号而不会在字符串末尾添加破折号?
正则表达式的前瞻`(?=..)`部分是一个断言,用来检查当前匹配点之后的内容。具体来说,`(?=..)`要求在当前匹配的位置之后至少还有两个字符。这种方式确保了只有在至少后面还有两个数字的情况下才会在匹配的数字后添加破折号。这样,当字符串末尾少于三个数字且无法满足该前瞻条件时,就不会添加破折号,避免了在末尾产生非预期的破折号。
🦆
对于输入字符串长度极短(例如只含一个数字或两个数字)的情况,此算法如何处理?是否有必要添加特定的条件来处理这些情况?
对于极短的输入字符串,例如只有一个或两个数字,正则表达式`(...?(?=..))`中的前瞻`(?=..)`无法满足,因此这部分正则表达式不会执行任何匹配,也就不会添加任何破折号。在这种情况下,`re.sub`简单地返回原始数字字符串,因为没有满足条件的部分来插入破折号。因此,算法已经能够适应这些特殊情况,无需额外添加特定条件。

相关问题