LeeCode_12整数转罗马数字
- 整数转罗马数字
七个不同的符号代表罗马数字,其值如下:
符号 值
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 解决了 “查找表在编译期生成” 的问题。
-
核心定义,隐式构造:在 C++ 标准库中,string 的某些构造函数被声明为了 constexpr,当编译器看到 "III" 这个字符串字面量时,它不仅仅是在运行时创建一个对象,而是可以在编译阶段就直接调用 string 的构造函数,生成一个 string 对象,并把它安放在只读内存(或者编译器的临时存储区)中。
在这个用法中,constexpr 强迫编译器必须把这个二维数组的初始化工作在编译期完成。 -
这是该用法最大的价值所在是:
时间开销非常小,运行时Zero-overhead (零开销),没有 constexpr:程序启动时,当流程走到这个变量定义的地方,CPU 需要执行指令,把一个个字符串拷贝到内存中,构建这个数组。这需要消耗 CPU 时间;有了 constexpr:编译器在编译代码时,就已经计算好了所有数据的二进制布局。程序运行时,这个数组就像全局静态字符串一样,直接加载进内存,不需要执行任何初始化代码。 -
为了让这种用法生效,这个数组 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;
}
};

浙公网安备 33010602011771号