Medium

题目描述

我们有一些二维坐标,如 "(1, 3)""(2, 0.5)"。然后,我们移除所有逗号、小数点和空格,得到字符串 s

  • 例如,"(1, 3)" 变成 s = "(13)""(2, 0.5)" 变成 s = "(205)"

返回一个字符串列表,表示我们原始坐标可能的所有情况。

我们的原始表示从来没有多余的零,所以我们从来不会以 "00""0.0""0.00""1.0""001""00.01" 或任何其他可以用更少数字表示的数字开始。同样,数字中的小数点前至少要有一位数字,所以我们从来不会以 ".1" 这样的数字开始。

最终答案列表可以按任何顺序返回。最终答案中的所有坐标在它们之间有恰好一个空格(出现在逗号之后)。

示例 1:

输入:s = "(123)"
输出:["(1, 2.3)","(1, 23)","(1.2, 3)","(12, 3)"]

示例 2:

输入:s = "(0123)"
输出:["(0, 1.23)","(0, 12.3)","(0, 123)","(0.1, 2.3)","(0.1, 23)","(0.12, 3)"]
解释:0.0, 00, 0001 或 00.01 是不允许的。

示例 3:

输入:s = "(00011)"
输出:["(0, 0.011)","(0.001, 1)"]

约束条件:

  • 4 <= s.length <= 12
  • s[0] == '('s[s.length - 1] == ')'
  • s 的其余部分都是数字。

解题思路

这道题的核心思路是枚举所有可能的坐标分割方式,然后对每一部分生成所有有效的数字表示。

解题步骤:

  1. 分割字符串:去掉首尾的括号后,枚举所有可能的分割点,将字符串分成两部分作为 x 坐标和 y 坐标。

  2. 生成有效数字:对每一部分,生成所有可能的有效数字表示(整数或小数)。需要遵循以下规则:

    • 整数不能有前导零(除非是单独的"0")
    • 小数的整数部分不能有前导零(除非是单独的"0")
    • 小数的小数部分不能有后缀零
  3. 验证函数:实现一个函数来判断数字字符串是否有效:

    • 对于整数:不能有前导零(“0"除外)
    • 对于小数:整数部分和小数部分都要符合规则
  4. 组合结果:将所有有效的 x 坐标和 y 坐标组合,形成最终结果。

具体实现策略:

  • 先写一个辅助函数生成单个数字串的所有有效表示
  • 然后枚举所有分割点,对左右两部分分别调用辅助函数
  • 最后将所有组合拼接成坐标格式

代码实现

class Solution {
public:
    vector<string> ambiguousCoordinates(string s) {
        vector<string> result;
        string nums = s.substr(1, s.length() - 2); // 去掉括号
        
        for (int i = 1; i < nums.length(); i++) {
            string left = nums.substr(0, i);
            string right = nums.substr(i);
            
            vector<string> leftCandidates = generateNumbers(left);
            vector<string> rightCandidates = generateNumbers(right);
            
            for (const string& l : leftCandidates) {
                for (const string& r : rightCandidates) {
                    result.push_back("(" + l + ", " + r + ")");
                }
            }
        }
        
        return result;
    }
    
private:
    vector<string> generateNumbers(const string& s) {
        vector<string> result;
        
        // 作为整数
        if (isValidInteger(s)) {
            result.push_back(s);
        }
        
        // 作为小数
        for (int i = 1; i < s.length(); i++) {
            string intPart = s.substr(0, i);
            string fracPart = s.substr(i);
            
            if (isValidInteger(intPart) && isValidFraction(fracPart)) {
                result.push_back(intPart + "." + fracPart);
            }
        }
        
        return result;
    }
    
    bool isValidInteger(const string& s) {
        return s == "0" || (s[0] != '0' && !s.empty());
    }
    
    bool isValidFraction(const string& s) {
        return !s.empty() && s.back() != '0';
    }
};
class Solution:
    def ambiguousCoordinates(self, s: str) -> List[str]:
        result = []
        nums = s[1:-1]  # 去掉括号
        
        for i in range(1, len(nums)):
            left = nums[:i]
            right = nums[i:]
            
            left_candidates = self.generate_numbers(left)
            right_candidates = self.generate_numbers(right)
            
            for l in left_candidates:
                for r in right_candidates:
                    result.append(f"({l}, {r})")
        
        return result
    
    def generate_numbers(self, s):
        result = []
        
        # 作为整数
        if self.is_valid_integer(s):
            result.append(s)
        
        # 作为小数
        for i in range(1, len(s)):
            int_part = s[:i]
            frac_part = s[i:]
            
            if self.is_valid_integer(int_part) and self.is_valid_fraction(frac_part):
                result.append(int_part + "." + frac_part)
        
        return result
    
    def is_valid_integer(self, s):
        return s == "0" or (s and s[0] != "0")
    
    def is_valid_fraction(self, s):
        return s and s[-1] != "0"
public class Solution {
    public IList<string> AmbiguousCoordinates(string s) {
        var result = new List<string>();
        string nums = s.Substring(1, s.Length - 2); // 去掉括号
        
        for (int i = 1; i < nums.Length; i++) {
            string left = nums.Substring(0, i);
            string right = nums.Substring(i);
            
            var leftCandidates = GenerateNumbers(left);
            var rightCandidates = GenerateNumbers(right);
            
            foreach (string l in leftCandidates) {
                foreach (string r in rightCandidates) {
                    result.Add($"({l}, {r})");
                }
            }
        }
        
        return result;
    }
    
    private List<string> GenerateNumbers(string s) {
        var result = new List<string>();
        
        // 作为整数
        if (IsValidInteger(s)) {
            result.Add(s);
        }
        
        // 作为小数
        for (int i = 1; i < s.Length; i++) {
            string intPart = s.Substring(0, i);
            string fracPart = s.Substring(i);
            
            if (IsValidInteger(intPart) && IsValidFraction(fracPart)) {
                result.Add(intPart + "." + fracPart);
            }
        }
        
        return result;
    }
    
    private bool IsValidInteger(string s) {
        return s == "0" || (s.Length > 0 && s[0] != '0');
    }
    
    private bool IsValidFraction(string s) {
        return s.Length > 0 && s[s.Length - 1] != '0';
    }
}
var ambiguousCoordinates = function(s) {
    const str = s.slice(1, -1);
    const result = [];
    
    function isValid(num) {
        if (num.length === 1) return true;
        if (num.includes('.')) {
            const parts = num.split('.');
            if (parts[0].length > 1 && parts[0][0] === '0') return false;
            if (parts[1][parts[1].length - 1] === '0') return false;
            return true;
        }
        return num[0] !== '0';
    }
    
    function getValidNumbers(s) {
        const numbers = [];
        if (isValid(s)) numbers.push(s);
        
        for (let i = 1; i < s.length; i++) {
            const num = s.slice(0, i) + '.' + s.slice(i);
            if (isValid(num)) numbers.push(num);
        }
        
        return numbers;
    }
    
    for (let i = 1; i < str.length; i++) {
        const left = str.slice(0, i);
        const right = str.slice(i);
        
        const leftNums = getValidNumbers(left);
        const rightNums = getValidNumbers(right);
        
        for (const l of leftNums) {
            for (const r of rightNums) {
                result.push(`(${l}, ${r})`);
            }
        }
    }
    
    return result;
};

复杂度分析

复杂度类型大小说明
时间复杂度O(n³)其中 n 是字符串长度。需要枚举 O(n) 个分割点,每个分割点生成 O(n) 个数字候选,组合时需要 O(n²)
空间复杂度O(n³)存储所有可能的数字组合和最终结果