📘 題目敘述
給你一個羅馬數字字串 s,請把它轉換成整數並回傳。
羅馬數字使用下列七個不同的符號表示,並對應不同的數值:
I= 1V= 5X= 10L= 50C= 100D= 500M= 1000
例如 2 寫成 II,也就是 2 個 1 相加。12
寫成 XII,也就是 X + II。
通常情況下,羅馬數字由左到右從大到小排列。但有六種特殊情況會用「減法表示」:
-
I可以放在V(5)和X(10)前面,形成IV(4)和IX(9) -
X可以放在L(50)和C(100)前面,形成XL(40)和XC(90) -
C可以放在D(500)和M(1000)前面,形成CD(400)和CM(900)
題目保證 s 是一個有效的羅馬數字,範圍在
1 到 3999。
條件限制
1 <= s.length <= 15-
s只包含字元('I', 'V', 'X', 'L', 'C', 'D', 'M') -
題目保證
s是有效的羅馬數字,範圍在[1, 3999]
🧠 解題思路
這題最重要的地方是「減法表示」:像 IV 其實不是
1 + 5,而是 5 - 1。
我用從左到右掃描的方式處理,並且只記住「上一個符號的值」就夠了。
我用 before 表示上一個羅馬符號的數值,now
表示目前這個符號的數值。每次讀到一個新符號後:
-
一般情況:
now <= before
代表是正常的由大到小排列,我直接把now加到答案上。 -
減法情況:
now > before
代表上一個符號其實應該要被減掉(例如IV讀到V時now=5、before=1)。
但我前一步其實已經把before加進ans了,所以現在要把它修正回「減法」效果:
目標應該是:ans這一段貢獻要變成now - before
但目前ans早就變成... + before + now
所以我要做的修正是:再加上-2 * before,也就是:ans = ans + now - 2 * before
這就是你程式裡那行的由來。
至於怎麼把字元轉成數值,我用兩個陣列 sym /
val
做對照,每次遇到字元就去找它是哪個符號,拿到對應的值。
所有變數
now:目前掃到的羅馬符號對應的數值before:上一個掃到的羅馬符號對應的數值ans:累加後的整數答案-
val:大小為 7 的陣列,依序存I,V,X,L,C,D,M的數值 -
sym:大小為 7 的陣列,依序存羅馬符號I,V,X,L,C,D,M
🪜 主要流程步驟
1. 先準備羅馬符號對照表,並初始化狀態
我先用 sym /
val 建立符號到數值的對應。然後把
ans 初始化為 0,並用
before = 10000
當作一個「比所有符號都大」的初始值,避免一開始就被判成減法。
2. 從左到右掃描字串,找出每個字元對應的數值 now
對每個字元 c,我用一個小迴圈去對照
sym,找到相同符號後就把
now = val[i] 設好,代表目前符號的數值。
3. 根據 now 和
before 判斷是正常加法還是減法修正
如果 now > before,代表出現像 IV、IX
這種減法情況。因為上一輪已經把 before 加進
ans 了,所以我要用:ans = ans + now - 2 * before
把 +before 修正成 -before。
如果
now <= before,代表正常排列,直接做:ans += now
4. 更新 before,準備處理下一個字元
每處理完一個字元,我就把
before = now,讓下一輪可以用它來判斷是否出現減法。
5. 回傳 ans
掃描結束後回傳答案。
💻 程式碼實作
class Solution {
public:
int romanToInt(string s) {
int now = 0, before = 10000;
int ans = 0;
int val[7] = {1, 5, 10, 50, 100, 500, 1000};
char sym[7] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
for (char c : s) {
for (int i = 0; i < 7; i++) {
if (c == sym[i]) {
now = val[i];
if (now > before) {
ans = ans + now - 2 * before;
} else {
ans += now;
}
before = now;
break;
}
}
}
return ans;
}
};
🔗 題目連結
https://leetcode.com/problems/roman-to-integer/