当前位置: 首页 > news >正文

12-罗马数字转整数

罗马数字包含以下七种字符: I, V, X, LCD 和 M

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1 。12 写做 XII ,即为 X + II 。 27 写做  XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。

方法一

要将罗马数字转换为整数,可以通过遍历罗马数字字符串,根据字符对应的数值以及罗马数字的规则来计算最终的整数结果。对于正常情况(小数字在大数字右边),直接累加对应数值;对于特殊情况(小数字在大数字左边),需要用大数字减去小数字。

function romanToInt(s: string): number {
    // 定义罗马数字字符与对应数值的映射
    const romanMap: { [key: string]: number } = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    };

    let result = 0;
    for (let i = 0; i < s.length; i++) {
        // 获取当前字符对应的数值
        const currentValue = romanMap[s[i]];
        // 如果不是最后一个字符,并且当前字符对应的数值小于下一个字符对应的数值
        if (i < s.length - 1 && currentValue < romanMap[s[i + 1]]) {
            // 表示是特殊情况,用下一个字符的数值减去当前字符的数值
            result += romanMap[s[i + 1]] - currentValue;
            // 由于已经处理了当前字符和下一个字符,跳过下一个字符
            i++;
        } else {
            // 正常情况,直接累加当前字符对应的数值
            result += currentValue;
        }
    }

    return result;
}

// 示例调用
const romanNumeral = "MCMXCIV";
const integerValue = romanToInt(romanNumeral);
console.log(`罗马数字 ${romanNumeral} 转换为整数是: ${integerValue}`);

代码解释

  1. 定义映射:创建一个对象 romanMap,将罗马数字字符映射到对应的整数值。
  2. 初始化结果变量result 用于存储最终转换得到的整数,初始化为 0。
  3. 遍历罗马数字字符串
    • 对于每个字符,获取其对应的数值 currentValue
    • 检查是否是特殊情况:如果当前字符不是字符串的最后一个字符,并且当前字符对应的数值小于下一个字符对应的数值,说明是特殊情况(如 IVIX 等),此时用下一个字符的数值减去当前字符的数值,并累加到 result 中,同时跳过下一个字符(因为已经处理过了)。
    • 若不是特殊情况,直接将当前字符对应的数值累加到 result 中。
  4. 返回结果:遍历结束后,result 即为转换后的整数,将其返回。
复杂度分析
  • 时间复杂度:O(n),其中  是罗马数字字符串的长度。因为只需要对字符串进行一次遍历。
  • 空间复杂度:O(1),只使用了常数级的额外空间,主要用于存储 romanMap 对象。 

方法二 

思路分析

可以先将特殊的组合(如 IVIX 等)提前处理,把它们替换为一个特殊的单字符,并且给这个特殊字符赋予对应的数值。然后遍历处理后的字符串,将每个字符对应的数值累加起来,从而得到最终的整数结果。

代码实现

function romanToInt(s: string): number {
    // 定义特殊组合及其对应数值的映射
    const specialMap: { [key: string]: [string, number] } = {
        "IV": ["#", 4],
        "IX": ["$", 9],
        "XL": ["%", 40],
        "XC": ["^", 90],
        "CD": ["&", 400],
        "CM": ["@", 900]
    };

    // 替换特殊组合
    for (const key in specialMap) {
        if (s.includes(key)) {
            const [replacement, value] = specialMap[key];
            s = s.replace(key, replacement);
        }
    }

    // 定义单个罗马字符及其对应数值的映射
    const romanMap: { [key: string]: number } = {
        "I": 1,
        "V": 5,
        "X": 10,
        "L": 50,
        "C": 100,
        "D": 500,
        "M": 1000,
        "#": 4,
        "$": 9,
        "%": 40,
        "^": 90,
        "&": 400,
        "@": 900
    };

    let result = 0;
    // 遍历处理后的字符串,累加每个字符对应的数值
    for (const char of s) {
        result += romanMap[char];
    }

    return result;
}

// 示例调用
const romanNumeral = "MCMXCIV";
const integerValue = romanToInt(romanNumeral);
console.log(`罗马数字 ${romanNumeral} 转换为整数是: ${integerValue}`);

代码解释

  1. 特殊组合映射:创建 specialMap 对象,将特殊的罗马数字组合(如 IVIX 等)映射为一个特殊字符和对应的数值。例如,IV 映射为 ["#", 4],表示用 # 替换 IV,且其代表的数值是 4。
  2. 替换特殊组合:遍历 specialMap,如果罗马数字字符串 s 中包含某个特殊组合,就用对应的特殊字符进行替换。
  3. 单个字符映射:创建 romanMap 对象,将单个罗马字符(包括替换后的特殊字符)映射到对应的整数值。
  4. 遍历累加:遍历处理后的字符串 s,将每个字符对应的数值累加到 result 中。
  5. 返回结果:最终返回 result,即转换后的整数。
复杂度分析
  • 时间复杂度:O(n),其中  是罗马数字字符串的长度。虽然有替换特殊组合的操作,但由于特殊组合的数量是固定的(6 种),替换操作的时间复杂度可以看作常数。主要的时间开销在于遍历处理后的字符串,所以整体时间复杂度为 。
  • 空间复杂度:O(1),使用的额外空间主要是存储映射关系的对象,这些对象的大小是固定的,不随输入字符串长度的增加而增加,因此空间复杂度为常数级。

这种方法的优势在于代码逻辑相对清晰,将特殊组合的处理和单个字符的处理分开,便于理解和扩展。如果后续需要添加更多的特殊规则,只需要修改 specialMap 和 romanMap 即可。

其他解法

基于反向遍历的解法

前面的方法多是正向遍历,其实反向遍历罗马数字字符串也能解决问题。反向遍历时,若当前字符对应的数值不小于前一个已处理字符对应的数值,就累加该数值;若小于,则减去该数值。

function romanToInt(s: string): number {
    const romanMap: { [key: string]: number } = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    };
    let result = 0;
    let prevValue = 0;
    // 从字符串末尾开始反向遍历
    for (let i = s.length - 1; i >= 0; i--) {
        const currentValue = romanMap[s[i]];
        if (currentValue >= prevValue) {
            result += currentValue;
        } else {
            result -= currentValue;
        }
        prevValue = currentValue;
    }
    return result;
}
复杂度分析
  • 时间复杂度:O(n),这里的  是字符串 s 的长度,因为只对字符串进行了一次反向遍历。
  • 空间复杂度:O(1),只使用了常数级的额外变量来存储数值和结果。

递归解法

递归也是一种可行的思路,把罗马数字字符串的转换问题分解成子问题。如果字符串长度为 1,直接返回对应字符的数值;如果字符串长度大于 1,先判断前两个字符是否构成特殊组合,若是则处理该组合并递归处理剩余部分,若不是则处理第一个字符并递归处理剩余部分。

function romanToInt(s: string): number {
    const romanMap: { [key: string]: number } = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000,
        'IV': 4,
        'IX': 9,
        'XL': 40,
        'XC': 90,
        'CD': 400,
        'CM': 900
    };

    if (s.length === 0) {
        return 0;
    }
    if (s.length === 1) {
        return romanMap[s];
    }
    const twoChar = s.slice(0, 2);
    if (romanMap[twoChar]) {
        return romanMap[twoChar] + romanToInt(s.slice(2));
    } else {
        return romanMap[s[0]] + romanToInt(s.slice(1));
    }
}
复杂度分析
  • 时间复杂度:O(n),因为每次递归调用都会处理一个或两个字符,最终遍历完整个字符串。
  • 空间复杂度:O(n),递归调用栈的深度最大为 ,其中  是字符串的长度。

基于规则匹配表的解法

构建一个规则匹配表,将罗马数字的字符组合及其对应的数值列出来,然后在遍历字符串时,根据规则匹配表来确定数值。

function romanToInt(s: string): number {
    const rules: [string, number][] = [
        ['M', 1000],
        ['CM', 900],
        ['D', 500],
        ['CD', 400],
        ['C', 100],
        ['XC', 90],
        ['L', 50],
        ['XL', 40],
        ['X', 10],
        ['IX', 9],
        ['V', 5],
        ['IV', 4],
        ['I', 1]
    ];
    let result = 0;
    let index = 0;
    while (index < s.length) {
        for (const [roman, value] of rules) {
            if (s.startsWith(roman, index)) {
                result += value;
                index += roman.length;
                break;
            }
        }
    }
    return result;
}
复杂度分析
  • 时间复杂度:O(n),其中  是字符串的长度。虽然有嵌套循环,但内层循环的规则数量是固定的,整体上每个字符最多被处理一次。
  • 空间复杂度:O(1),规则匹配表的大小是固定的,不随输入字符串长度的变化而变化。

 

相关文章:

  • DeepSeek R1 与 OpenAI O1:机器学习模型的巅峰对决
  • python(1)-元组和集合
  • linux-centos nginx 添加stream模块
  • Map和Set
  • FunPapers[2]:www‘24 「快手」连续特征单调性建模
  • 【RocketMQ 存储】消息重放服务-ReputMessageService
  • Java与DeepSeek的完美结合:开启高效智能编程新时代 [特殊字符]
  • JavaSE:数组
  • Pygame中自定义事件处理的方法2-2
  • Linux-文件IO
  • 蓝桥杯篇---8位 ADC/DAC转换芯片 PCF8591
  • AtCoder Beginner Contest 393 —— E - GCD of Subset 补题 + 题解 python
  • 2025-02-16 学习记录--C/C++-PTA 7-21 求特殊方程的正整数解
  • 【css】超过文本显示省略号
  • nodejs:express + js-mdict 网页查询英汉词典,能显示图片
  • 线程池有哪几种状态?
  • AI与SEO协同:智能关键词挖掘与精准部署策略
  • 以若依移动端版为基础,实现uniapp的flowable流程管理
  • 【Spring Boot】SpringBoot自动装配-Import
  • TestHubo简介与安装
  • 当“小铁人”遇上青浦,看00后如何玩转长三角铁三
  • 马上评|科学红毯,让科学家成为“最亮的星”
  • 湖南慈利一村干部用AI生成通知并擅自发布,乡纪委立案
  • 普京调整俄陆军高层人事任命
  • 恒生银行回应裁员传闻:受影响的员工数目占银行核心业务员工总数约1%
  • 中国人民银行等四部门联合召开科技金融工作交流推进会