1. 程式人生 > 實用技巧 >Leetcode之整數轉羅馬數字

Leetcode之整數轉羅馬數字

問題描述

羅馬數字包含以下七種字元: I, V, X, L,C,D 和 M。

字元 數值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

例如, 羅馬數字 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:

輸入: 3
輸出: "III"

示例 2:

輸入: 4
輸出: "IV"

示例 3:

輸入: 9
輸出: "IX"

示例 4:

輸入: 58
輸出: "LVIII"
解釋: L = 50, V = 5, III = 3.

示例 5:

輸入: 1994
輸出: "MCMXCIV"
解釋: M = 1000, CM = 900, XC = 90, IV = 4.

解法

分解數字中的每一位。選擇二維陣列(其實可以通過對映變為一維陣列),存放羅馬字元。

然後編寫一個getS(int num,int type)的函式,type既是個十百千又是二維陣列的行數。這樣將每種都統一起來。

class Solution {
  private static String[][] data=new String[4][4];

	  public static String intToRoman(int num) {
		  data[0][0]="I";
		  data[0][1]="V";
		  data[0][2]="IV";
		  data[0][3]="IX";
		  data[1][0]="X";
		  data[1][1]="L";
		  data[1][2]="XL";
		  data[1][3]="XC";
		  data[2][0]="C";
		  data[2][1]="D";
		  data[2][2]="CD";
		  data[2][3]="CM";
		  data[3][0]="M";
		  StringBuilder sb=new StringBuilder();
		  sb.append(getS(num/1000,3));
		  sb.append(getS(num/100%10,2));
		  sb.append(getS(num/10%10,1));
		  sb.append(getS(num%10,0));
		  return sb.toString();
	    }
	  public static String getS(int num,int type) {
		  if(num==0)
			  return "";
		  if(num==9)
			  return data[type][3];
		  if(num==4)
			  return data[type][2];
		  StringBuilder sb=new StringBuilder();
		  if(1<=num&&num<5) {
			  sb.append(data[type][0]);
			  for(int i=1;i<num;i++) {
				  sb.append(data[type][0]);
			  }
		  }else {
			  sb.append(data[type][1]);
			  for(int i=5;i<num;i++) {
				  sb.append(data[type][0]);
			  }
		  }
		  return sb.toString();
		  
	  }
}

結果

官方解法

方法一:貪心

將給定的整數轉換為羅馬數字需要找到上述 13 個符號的序列,這些符號的對應值加起來就是整數。根據符號值,此序列必須按從大到小的順序排列。符號值如下。

在程式碼中實現這一點的最簡單的方法是從最大到最小迴圈遍歷每個符號,檢查當前符號的有多少個副本適合剩餘的整數。

PythonJava
int[] values = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};    
String[] symbols = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};

public String intToRoman(int num) {
    StringBuilder sb = new StringBuilder();
    // Loop through each symbol, stopping if num becomes 0.
    for (int i = 0; i < values.length && num >= 0; i++) {
        // Repeat while the current symbol still fits into num.
        while (values[i] <= num) {
            num -= values[i];
            sb.append(symbols[i]);
        }
    }
    return sb.toString();
}

方法二:硬編碼數字

這種就是類似我的方法(但是我的效率好像更高)。把整數轉換成羅馬數字時,整數的十進位制表示中的每一個數字都可以單獨處理。所有的符號可以根據在 1000,100,10 和 1 的最大因子分成多個組。

public String intToRoman(int num) {
    
    String[] thousands = {"", "M", "MM", "MMM"};
    String[] hundreds = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"}; 
    String[] tens = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
    String[] ones = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
    
    return thousands[num / 1000] + hundreds[num % 1000 / 100] + tens[num % 100 / 10] + ones[num % 10];
}

方法三 人造雜湊表

在第二種方法的基礎上,因為每一位數字只有0-9十個數字。而且輸入的範圍0-3999,就可以構造出一張雜湊表。

class Solution {
public:
    string intToRoman(int num)
    {
        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;
        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;
    }
};

這樣做效率很高。