leetcode 精選top面試題 - 13. 羅馬數字轉整數
13. 羅馬數字轉整數
羅馬數字包含以下七種字元:I,V,X,L,C,D和M。
例如, 羅馬數字 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。
給定一個羅馬數字,將其轉換成整數。輸入確保在 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.
提示:
- 題目所給測試用例皆符合羅馬數字書寫規則,不會出現跨位等情況。
- IC 和 IM 這樣的例子並不符合題目要求,49 應該寫作 XLIX,999 應該寫作 CMXCIX 。
- 關於羅馬數字的詳盡書寫規則,可以參考 羅馬數字 - Mathematics 。
思路一:
把幾個字元的值存入hashMap,幾個特殊的值也存入hashmap 如果下個字元比當前字元分值小,直接加上當前字元的分值 如果下個字元比當前字元分值大,判斷這兩個連續的字元形成的子串是否是特殊子串; 如果是特殊子串,直接加上相對應的分值,位置後移一位 如果不是,直接加上當前字元的分值執行用時:15 ms, 在所有Java提交中擊敗了6.61%的使用者 記憶體消耗:39.4 MB, 在所有Java提交中擊敗了14.14%的使用者1 class Solution { 2 public int romanToInt(String s) { 3 4 // 把幾個字元的值存入hashMap, 幾個特殊的值也存入hashmap 5 HashMap<String, Integer> map = new HashMap<>(); 6 map.put("I", 1); 7 map.put("V", 5); 8 map.put("X", 10); 9 map.put("L", 50); 10 map.put("C", 100); 11 map.put("D", 500); 12 map.put("M", 1000); 13 map.put("IV", 4); 14 map.put("IX", 9); 15 map.put("XL", 40); 16 map.put("XC", 90); 17 map.put("CM", 900); 18 map.put("CD", 400); 19 20 int res = 0; 21 int len = s.length(); 22 // 遍歷字串 23 for(int i = 0; i < len; i++){ 24 // 如果下個字元比當前字元小,直接加上當前字元的值 25 if(i + 1 >= len || map.get(s.charAt(i) + "") >= map.get(s.charAt(i+1) + "")){ 26 res += map.get(s.charAt(i) + ""); 27 }else{ 28 // 如果下個字元比當前字元大,判斷這兩個連續的字元形成的子串是否是特殊子串 29 String temp = s.substring(i, i+2); 30 if(map.containsKey(temp)){ // 如果是特殊子串,直接加上相對應的值,位置後移一位 31 res += map.get(temp); 32 i++; 33 }else{ // 如果不是,直接加上當前字元的值 34 res += map.get(s.charAt(i)); 35 } 36 } 37 } 38 return res; 39 } 40 }
複雜度分析:
時間複雜度:遍歷了整個字串,所以時間複雜度為O(n)。
空間複雜度:O(1)。所有變數或者集合的大小都是常數級別的,所以空間複雜度為O(1)。
思路二:
判斷當前字元和下個字元形成的子串是否是特殊子串,如果是直接加上對應特殊子串的值,索引後移一位,否則直接就是單個字元對應的值,直接加上當前字元對應的值即可。
1 class Solution {
2 public int romanToInt(String s) {
3
4 // 把幾個字元的值存入hashMap, 幾個特殊的值也存入hashmap
5 HashMap<String, Integer> map = new HashMap<>();
6 map.put("I", 1);
7 map.put("V", 5);
8 map.put("X", 10);
9 map.put("L", 50);
10 map.put("C", 100);
11 map.put("D", 500);
12 map.put("M", 1000);
13 map.put("IV", 4);
14 map.put("IX", 9);
15 map.put("XL", 40);
16 map.put("XC", 90);
17 map.put("CM", 900);
18 map.put("CD", 400);
19
20 int res = 0;
21 int len = s.length();
22 // 遍歷字串
23 for(int i = 0; i < len; i++){
24 // 如果下個字元比當前字元小,直接加上當前字元的值
25 String cur = s.charAt(i) + "";
26 if(i + 1 >= len){
27 res += map.get(cur);
28 continue;
29 }
30 String next = s.charAt(i+1) + "";
31 String temp = s.substring(i, i+2);
32 // 判斷這兩個連續的字元形成的子串是否是特殊子串
33 if(map.containsKey(temp)){ // 如果是特殊子串,直接加上相對應的值,位置後移一位
34 res += map.get(temp);
35 i++;
36 }else{ // 如果不是,直接加上當前字元的值
37 res += map.get(s.charAt(i) + "");
38 }
39 }
40 return res;
41 }
42 }
leetcode 執行用時:18 ms, 在所有Java提交中擊敗了5.46%的使用者
記憶體消耗:39 MB, 在所有Java提交中擊敗了47.09%的使用者
思路三:對思路一的改進
思路一之所以需要在下個字元比當前字元分值大時,判斷這兩個連續的字元形成的子串是否是特殊子串後加上特殊子串的分值;而非直接減去當前字元的分值,是因為擔心有IC, IM等子串的出現,但是觀察題目之後,發現這些子串是不合法的羅馬數字,而題目要求是輸入合法的羅馬數字,所以不用考慮這些子串。所以把原來判斷這兩個連續的字元形成的子串是否是特殊子串後加上特殊子串的分值改成直接減去當前字元的分值。
1 class Solution {
2 public int romanToInt(String s) {
3
4 // 把幾個字元的值存入hashMap, 幾個特殊的值也存入hashmap
5 HashMap<Character, Integer> map = new HashMap<>();
6 map.put('I', 1);
7 map.put('V', 5);
8 map.put('X', 10);
9 map.put('L', 50);
10 map.put('C', 100);
11 map.put('D', 500);
12 map.put('M', 1000);
13
14 int res = 0;
15 int len = s.length();
16 // 遍歷字串
17 for(int i = 0; i < len; i++){
18
19 Character cur = s.charAt(i);
20 // 如果當前字元是最後一個字元,直接加上當前字元的值
21 if(i + 1 >= len){
22 res += map.get(cur);
23 continue;
24 }
25 Character next = s.charAt(i + 1);
26 // 如果是下個字元的分值大於當前字元的分值,說明是特殊情況,直接減去當前字元的分值
27 if(map.get(cur) < map.get(next)){
28 res -= map.get(cur);
29 }else{ // 如果不是特殊情況,直接加上當前字元的值
30 res += map.get(cur);
31 }
32 }
33 return res;
34 }
35 }
執行用時:7 ms, 在所有Java提交中擊敗了40.74%的使用者, 時間上縮短了很多,HashMap的鍵是字元,而非字串,少了字串的拼接開銷。
記憶體消耗:38.9 MB, 在所有Java提交中擊敗了58.91%的使用者
複雜度分析:
時間複雜度:遍歷了整個字串,所以時間複雜度為O(n)。
空間複雜度:O(1)。所有變數或者集合的大小都是常數級別的,所以空間複雜度為O(1)。
思路四:對思路二的改進
使用switch 表示式代替HashMap的鍵值對對映
1 class Solution {
2 public int romanToInt(String s) {
3
4 int res = 0;
5 int len = s.length();
6 int preScore = getScore(s.charAt(0));
7
8 // 遍歷字串
9 for(int i = 1; i < len; i++){
10 int curScore = getScore(s.charAt(i));
11 // 如果是上個字元的分值小於當前字元的分值,說明是特殊情況,直接減去當前字元的分值
12 if(preScore < curScore){
13 res -= preScore;
14 }else{ // 如果不是特殊情況,直接加上當前字元的值
15 res += preScore;
16 }
17 preScore = curScore;
18 }
19 res += preScore;
20 return res;
21 }
22
23 public int getScore(char ch){
24 switch(ch){
25 case 'I': return 1;
26 case 'V': return 5;
27 case 'X': return 10;
28 case 'L': return 50;
29 case 'C': return 100;
30 case 'D': return 500;
31 case 'M': return 1000;
32 default: return 0;
33 }
34 }
35 }
leetcode執行用時:4 ms, 在所有Java提交中擊敗了99.98%的使用者, 避免對HashMap這種集合的操作,效率再次上升。
記憶體消耗:38.6 MB, 在所有Java提交中擊敗了88.19%的使用者