羅馬數字轉整數
題目
羅馬數字包含以下七種字符: I, V, X, L,C,D 和 M。
字符 數值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 羅馬數字 2 寫做 ,即爲兩個並列的 1。12 寫做 ,即爲 。 27 寫做 , 即爲 。
通常情況下,羅馬數字中小的數字在大的數字的右邊。但也存在特例,例如 4 不寫做 ,而是 。數字 1 在數字 5 的左邊,所表示的數等於大數 5 減小數 1 得到的數值 4 。同樣地,數字 9 表示爲 。這個特殊的規則只適用於以下六種情況:
- 可以放在 (5) 和 (10) 的左邊,來表示 4 和 9。
- 可以放在 (50) 和 (100) 的左邊,來表示 40 和 90。
- 可以放在 (500) 和 (1000) 的左邊,來表示 400 和 900。
給定一個羅馬數字,將其轉換成整數。輸入確保在 1 到 3999 的範圍內。
示例 1:
輸入: "III"
輸出: 3
示例 2:
輸入: "IV"
輸出: 4
示例 3:
輸入: "IX"
輸出: 9
示例 4:
輸入: "LVIII"
輸出: 58
解釋: L = 50, V= 5, III = 3.
示例 5:
輸入: "MCMXCIV"
輸出: 1994
解釋: M = 1000, CM = 900, XC = 90, IV = 4.
函數原型
C的函數原型:
int romanToInt(char * s){}
邊界判斷
題目裏有說,輸入確保在 1 到 3999 的範圍內。
不過這個怎麼判斷呢,用戶輸入的是羅馬數字,而不是阿拉伯數字…
你問我,我也不知道啊。
但可以對輸入參數做檢查。
if( s == NULL || *s == '\0') // 指針是否爲NULL
return 0;
算法設計:查表法
因爲只包含了 7 個數字,我們可以建一個表來映射羅馬數字與阿拉伯數字之間的關係。
int map[90] = {'\0'};
// 建表, 來映射數字間的關係;大寫字母的範圍是 65(A)-90(Z)
字符 數值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
建表
map['I'] = 1;
map['V'] = 5;
map['X'] = 10;
map['L'] = 50;
map['C'] = 100;
map['D'] = 500;
map['M'] = 1000;
讀了示例後,發現羅馬數字主要有倆種情況(從左往右看):
- 左加:左的數字(前一個數)比右邊的數字(後一個數)大、相等時,加上前一個數
- 右減:左的數字(前一個數)比右邊的數字(後一個數)小時,減去前一個數
例如,。
,[前一個數] 比 [後一個數] 小,就要減掉前一個數。
int romanToInt(char * s){
if( s == NULL || *s == '\0') // 指針是否爲NULL
return 0;
int map[90] = {'\0'};
// 建表, 來映射數字間的關係
map['I'] = 1;
map['V'] = 5;
map['X'] = 10;
map['L'] = 50;
map['C'] = 100;
map['D'] = 500;
map['M'] = 1000;
int Roman_val = 0; // 定義一個變量,保存羅馬數字轉換後的值
// 倆種情況,分別討論
for(int i=0; i<strlen(s); i++) // 從左往右看
if( map[s[i]] >= map[s[i+1]] ) // 左加(前一個數 >= 後一個數)
Roman_val += map[s[i]];
else // 右減(前一個數 < 後一個數)
Roman_val -= map[s[i]];
return Roman_val;
}
AC。
查表法的複雜度:
- 時間複雜度:
- 空間複雜度:
查表法是使用空間換時間,這個題目需要的空間很小,畢竟只有 7 種狀態。
而大部分能使用查表法的題目,可能就需要大量的空間。雖然會讓時間複雜度很好,但佔用內存太多了。
在時間複雜度上,查表是 。
但是,在真實的計算機中,內存和處理器之間還有一個高速緩存,程序和數據要先從內存進入高速緩存,才能運行。
高速緩存的空間非常有限,通常只有幾兆(),查表佔用的內存空間可能是緩存容量的上千倍,這肯定是放不下的,遇到這種情況,計算機本身要進行上千次額外操作,把內存的內容往緩存倒騰。
也就是說,如果建立一個大表,雖然查表只需要做一次,但是準備工作可能要做上千次。
其實划不來,當然,也有一種補救的方法。
把一張大表拆分爲幾個小表,每個表查找一次,再把幾次的相加,雖然查找次數多了,但佔用的內存就很少的,這樣就即有查表法的優點(查表時間複雜度是 ),又補救了佔用大量內存空間的缺陷。
算法設計:過程模擬
研究【右減】的情況,前一個數比後一個數小,就要減掉前一個數。
而這個規則只適用於以下六種情況:
- 可以放在 (5) 和 (10) 的左邊,來表示 4 和 9。
- 可以放在 (50) 和 (100) 的左邊,來表示 40 和 90。
- 可以放在 (500) 和 (1000) 的左邊,來表示 400 和 900。
如果第 個元素是 ,第 個元素比第 個元素大,那就只有 。
如果第 個元素是 ,第 個元素比第 個元素大,那就只有 。
如果第 個元素是 ,第 個元素比第 個元素大,那就只有 。
int romanToInt(char * s){
int count = 0;
while (*s){
// 左加(前一個數比後一個數大)
if (*s == 'V') count += 5;
else if (*s == 'L') count += 50;
else if (*s == 'D') count += 500;
else if (*s == 'M') count += 1000;
// 右減(前一個數比後一個數小,就要減掉前一個數)
else if (*s == 'I')
count = (*(s + 1) == 'V' || *(s + 1) == 'X') ? count - 1 : count + 1;
else if (*s == 'X')
count = (*(s + 1) == 'L' || *(s + 1) == 'C') ? count - 10 : count + 10;
else if (*s == 'C')
count = (*(s + 1) == 'D' || *(s + 1) == 'M') ? count - 100 : count + 100;
s++;
}
return count;
}
過程模擬複雜度:
- 時間複雜度:
- 空間複雜度: