原题链接
题目内容太长就不放上来了。我的思路就是遍历字符串,找到每个罗马数字和整数的对应关系,然后累加这些整数——这其实蛮简单的。这题的关键有:
- 怎么尽可能快地找到罗马字母和数字的对应关系?
- 特殊情况就是当小的罗马数字在大的罗马数字之前时,数值的计算有变化。这个变化怎么特殊处理?
我的解法可以说是“杀鸡蔫用牛刀”了。莫名其妙搞一个栈出来,就是为了将栈顶元素(当前罗马数字的前一个)和当前遍历到的罗马数字进行比较。而且搞这个栈的理由还很憨憨:就是为了避免找上一个和下一个罗马字母的时候会出现下标越界的情况…其实想想这个理由也很蠢罢了——实际上避免越界完全可以用控制遍历的条件来做到…
class Solution {
public int romanToInt(String s) {
int num = 0;
Deque<Character> sChar = new ArrayDeque<>();
int addNum = 0;
//使用Deque可以避免使用charAt超出范围的异常
for (int i=0;i<s.length();i++){
switch (s.charAt(i)){
case 'I': addNum = 1;break;
case 'V':{
if(sChar.peek()!=null&&sChar.peek().equals('I'))
addNum = 3;
else
addNum = 5;
}break;
case 'X':{
if(sChar.peek()!=null&&sChar.peek().equals('I'))
addNum = 8;
else
addNum = 10;
}break;
case 'L':{
if(sChar.peek()!=null&&sChar.peek().equals('X'))
addNum = 30;
else
addNum = 50;
}break;
case 'C':{
if(sChar.peek()!=null&&sChar.peek().equals('X'))
addNum = 80;
else
addNum = 100;
}break;
case 'D':{
//注意空指针异常
if(sChar.peek()!=null&&sChar.peek().equals('C'))
addNum = 300;
else
addNum = 500;
}break;
case 'M':{
if(sChar.peek()!=null&&sChar.peek().equals('C'))
addNum = 800;
else
addNum = 1000;
}break;
}
num += addNum;
sChar.push(s.charAt(i));
}
return num;
}
}
我认为我这题没有把握到正解的原因还是没有好好把握对特殊情况的处理方法。如果是一般的罗马数字放在当前罗马数字前面,那就没什么好说的,直接累加就好了。但如果是较小的罗马数字放在较大的罗马数字前面,那就要用较大的罗马数字去减去较小的罗马数字了。
还有就是怎么处理罗马数字和整数的对应关系呢?官方解答使用map存储,然后用map的get方法获得对应的整数。有的大神用switch也很好。这里贴出两个优秀解答。
import java.util.*;
class Solution {
public int romanToInt(String s) {
int sum = 0;
int preNum = getValue(s.charAt(0));
for(int i = 1;i < s.length(); i ++) {//遍历直接从下标1的罗马字符开始,这样就避免了越界
int num = getValue(s.charAt(i));
if(preNum < num) {
sum -= preNum;
} else {
sum += preNum;
}
//以上处理完当前遍历数的前一个数
preNum = num;//更新“当前遍历数的前一个数”
}
sum += preNum;//加上最后一个数
return sum;
}
private int getValue(char ch) {
switch(ch) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return 0;
}
}
}
题目的提示说明了可以使用map结构。所以官方的解答也使用了map结构,用数据结构处理不失为坏事,但是这里使用map多少也有点大材小用的意思,与switch相比还占用了内存。而且在if条件中还要额外多一个条件判断下标越界,就有点笨拙了。
class Solution {
Map<Character, Integer> symbolValues = new HashMap<Character, Integer>() {{
put('I', 1);
put('V', 5);
put('X', 10);
put('L', 50);
put('C', 100);
put('D', 500);
put('M', 1000);
}};//成员变量应该写在方法之上。
public int romanToInt(String s) {
int ans = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
int value = symbolValues.get(s.charAt(i));
if (i < n - 1 && value < symbolValues.get(s.charAt(i + 1))) {
//if条件i<n-1判断避免越界
ans -= value;
} else {
ans += value;
}
}
return ans;
}
}
还有人更加精益求精,提出使用String类的API:replace避免条件判断。这就更加高效了。只要String的库函数不是放在循环中,性能的损耗就是可以接受了。而避免了条件判断让性能提升非常明显。
class Solution {
public int romanToInt(String s) {
s = s.replace("IV","a");
s = s.replace("IX","b");
s = s.replace("XL","c");
s = s.replace("XC","d");
s = s.replace("CD","e");
s = s.replace("CM","f");
int result = 0;
for (int i=0; i<s.length(); i++) {
result += which(s.charAt(i));
}
return result;
}
public int which(char ch) {
switch(ch) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
case 'a': return 4;
case 'b': return 9;
case 'c': return 40;
case 'd': return 90;
case 'e': return 400;
case 'f': return 900;
}
return 0;
}
}