Roman to Integer
- Easy
- keyword: Pattern
- 类似: Gray, Digit Counts, rotate graph, Max Tree, Triangle Sum,
羅馬數字共有7個,即I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和M(1000)。按照下述的規則可以表示任意正整數。需要注意的是罗马数字中没有“0”,與進位制無關。一般認為羅馬數字只用來記數,而不作演算。
- 重複數次:一個羅馬數字重複幾次,就表示這個數的幾倍。
- 右加左減:
- 在較大的羅馬數字的右邊記上較小的羅馬數字,表示大數字加小數字。
- 在較大的羅馬數字的左邊記上較小的羅馬數字,表示大數字减小數字。
- 左减的数字有限制,仅限于I、X、C。比如45不可以写成VL,只能是XLV
思考
- 这种就是通过找到Pattern来解决问题, 类似的有Gray code, Digit Counts, rotate graph. 都是理解题意, 找到规律, 然后解决问题.
- 同时也可以发现反过来解决比正向更好解决, 这就类似Max Tree, Triangle Sum, 乃至于所有top-down/bottom-up的recursion.
idea 1: 正向loop
- 既然题目说的左小就减小:
IV = 5-1
, 右小就加小:VI = 5 + 1
. 那么从头过一遍, 比较前一个数和当前的数, 若前小就相减, 若前大就相加. 注意这里已经和题意有点区别, 但是是等价的, 因为这样才好loop解决. - 正如: maples7分析的: 由于result holder在i的时候, 已经加入了s[i-1]了, 所以相减的时候要减去之前加了的s[i-1]. 对 s[i-2] 分情况讨论便知.
- 下面举2个例子:
- 简单的: MMXII, 由于是递减的, 所以都是第一种情况, 即大的罗马数字右边接一个小的, 所以加就好了. 即
M+M+X+I+I = 1000 + 1000 + 10 + 1 + 1 = 2012
- 来一个综合的例子:
- 简单的: MMXII, 由于是递减的, 所以都是第一种情况, 即大的罗马数字右边接一个小的, 所以加就好了. 即
- 代码如下: MCMXCVI. 注意有CM, XC. 这些是左边接小的罗马数字, 所以相减, 即
M+(CM)+(XC)+V+I = M + (M-C) + (C-X) + V + I = 1000 + (1000-100) + (100-10) + 5 + 1 = 1996
.
public int romanToIntPosOrd(String s) {
if (s == null || s.length() == 0) return 0;
Map<Character, Integer> map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000);
int len = s.length();
char[] Arr = s.toCharArray();
int res = map.get(Arr[0]);
for (int i = 1; i < len; ++i) {
if (map.get(Arr[i - 1]) <= map.get(Arr[i])) {
res += map.get(Arr[i]) - 2 * map.get(Arr[i-1]); // 注意此时要剪掉i-1的时候加上的Arr[i-1]
} else {
res += map.get(Arr[i]); // 此时加上Arr[i].
}
}
return res;
}
idea 2: 反向looping
- 由于正向loop的时候, 会有出现:
先是加了C, 结果发现下一个是M, 从而应该是CM组合后再加, 即加上M-C, 所以第一次加多了一次C, 从而出现了补救/修正的过程, 即: map.get(Arr[i]) - 2 * map.get(Arr[i-1])
. - 可以更好的解决问题吗?
- 一般来说: 大部分问题都可以正的/烦得解决. 所以看看反过来会怎样: 同样是以包含2种规则的例子:
MCMXCVI
为例: 从后向前是:I + V + C - X + M - C + M
, 这不是很完美吗? 不用修正, 只要看大小来确定是加/减. - 代码如下
public int romanToIntPosOrd(String s) {
if (s == null || s.length() == 0) return 0;
Map<Character, Integer> map = new HashMap<>(); // map过程如上, 就不重复了.
int len = s.length();
char[] Arr = s.toCharArray();
int res = map.get(Arr[len-1]);
for (int i = len-2; i >= 0; i--) {
if (map.get(Arr[i + 1]) <= map.get(Arr[i])) {
res += map.get(Arr[i]); // 比正向漂亮多了
} else {
res -= map.get(Arr[i]);
}
}
return res;
}