LeeCode_12整数转罗马数字

  1. 整数转罗马数字

七个不同的符号代表罗马数字,其值如下:

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

罗马数字是通过添加从最高到最低的小数位值的转换而形成的。将小数位值转换为罗马数字有以下规则:

  • 如果该值不是以 4 或 9 开头,请选择可以从输入中减去的最大值的符号,将该符号附加到结果,减去其值,然后将其余部分转换为罗马数字。
  • 如果该值以 4 或 9 开头,使用 减法形式,表示从以下符号中减去一个符号,例如 4 是 5 (V) 减 1 (I): IV ,9 是 10 (X) 减 1 (I):IX。仅使用以下减法形式:4 (IV),9 (IX),40 (XL),90 (XC),400 (CD) 和 900 (CM)。
  • 只有 10 的次方(I, X, C, M)最多可以连续附加 3 次以代表 10 的倍数。你不能多次附加 5 (V),50 (L) 或 500 (D)。如果需要将符号附加4次,请使用 减法形式。
    给定一个整数,将其转换为罗马数字。

示例 1:

输入:num = 3749

输出: "MMMDCCXLIX"

解释:

3000 = MMM 由于 1000 (M) + 1000 (M) + 1000 (M)

700 = DCC 由于 500 (D) + 100 (C) + 100 (C)

40 = XL 由于 50 (L) 减 10 (X)

9 = IX 由于 10 (X) 减 1 (I)

注意:49 不是 50 (L) 减 1 (I) 因为转换是基于小数位

1 <= num <= 3999

class Solution {
public:
 static constexpr string R[4][10] = {
            //static:静态成员,属于类而非对象,所有实例共享这一份存储。
            //constexpr:常量表达式。这意味着这个数组在编译期就会被初始化并存储在只读数据段,运行时没有任何初始化开销,效率极高。
            {"","I","II","III","IV","V","VI","VII","VIII","IX"},
            {"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"},
            {"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"},
            {"","M","MM","MMM"}//1 <= num <= 3999
        };

    string intToRoman(int num) {
        return R[3][num / 1000] + R[2][num / 100 % 10 ] + R[1][num / 10 % 10] + R[0][num % 10];
    }
};

补充:

C++11中新增加了用于指示常量表达式的constexpr关键字;

constexpr是修饰一个常量表达式。但请注意constexpr不是修饰常量表达式的唯一途径。

在修饰变量时没有必要同时使用const和constexpr 因为constexpr包含了 const的含义

constexpr const int N = 5;
constexpr int N = 5;
//两行的意思完全相同

值得注意的是,虽然constexpr关键字包含了const含义,但有些时候必须也要有const,例如:

static constexpr int N = 3;
int main()
{
    constexpr const int *NP = &N;
    //constexpr int *NP = &N; 会error,因为constxpr表示NP指针本身是常量表达式,而const白女士指向的值是一个常量。去掉const之后无法编译,因为不能用正常指针指向常量。
    return 0;
}

题目解题代码中: static constexpr 的用法

C++20:直到 C++20,std::string 才开始支持 constexpr 构造。
利用 constexpr 解决了 “查找表在编译期生成” 的问题。

  1. 核心定义,隐式构造:在 C++ 标准库中,string 的某些构造函数被声明为了 constexpr,当编译器看到 "III" 这个字符串字面量时,它不仅仅是在运行时创建一个对象,而是可以在编译阶段就直接调用 string 的构造函数,生成一个 string 对象,并把它安放在只读内存(或者编译器的临时存储区)中。在这个用法中,constexpr 强迫编译器必须把这个二维数组的初始化工作在编译期完成。

  2. 这是该用法最大的价值所在是:时间开销非常小,运行时Zero-overhead (零开销),没有 constexpr:程序启动时,当流程走到这个变量定义的地方,CPU 需要执行指令,把一个个字符串拷贝到内存中,构建这个数组。这需要消耗 CPU 时间;有了 constexpr:编译器在编译代码时,就已经计算好了所有数据的二进制布局。程序运行时,这个数组就像全局静态字符串一样,直接加载进内存,不需要执行任何初始化代码。

  3. 为了让这种用法生效,这个数组 R 本身必须声明为 static(静态成员变量)或全局变量。因为 constexpr 对象通常是存储在只读数据段(.rodata)的。如果它是一个普通的局部变量(存储在栈上),虽然 C++ 允许,但编译器很难在编译期就把数据“写死”在栈帧的代码里。结合 static,它成为了生命周期贯穿整个程序运行期的常量,这正是查找表的最佳存储方式。

法二:用更轻量的char *数组

class Solution {
public:
    string intToRoman(int num)
    {
       const char* c[4][10] = {
            {"","I","II","III","IV","V","VI","VII","VIII","IX"},
            {"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"},
            {"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"},
            {"","M","MM","MMM"}
        };
        string roman;//利用string的append函数,将字符串拼接到一起
        roman.append(c[3][num / 1000]);
        roman.append(c[2][num / 100 % 10]);
        roman.append(c[1][num / 10 % 10]);
        roman.append(c[0][num % 10]);
         
        return roman;
    }
};

 
posted @ 2026-01-15 23:32  Jaylan  阅读(1)  评论(0)    收藏  举报