LeetCode 13. Roman to Integer

Roman to Integer

Given a roman numeral, convert it to an integer.
Input is guaranteed to be within the range from 1 to 3999.

给定一个罗马数字,将其转换为整数。输入保证在1到3999的范围内。

个人思路:

说实话,刚看到这个题目的时候我是一脸茫然的。罗马数字?我似乎只认识1~9的罗马数字诶。然后开始无尽的查阅资料之旅。。。

罗马数字的定义:

罗马数字是符号和加操作的一个组合。他基于以下七个符号。

Symbol Value
I 1
V 5
X 10
X 10
L 50
C 100
D 500
M 1000

II 表示 2,XIII 表示13。罗马数字没有0,所以207是CCVII,1066是MLXVI.

计数规则:
  1. 若干相同数字连写表示的数是这些罗马数字的和,如 III=3;
  2. 小数字在大数字前面表示的数是用大数字减去小数字,如 IV=4;
  3. 小数字在大数字后面表示的数是用大数字加上小数字,如 VI=6;

其中每两个阶段的之间有一个减法的表示,比如 900=CM, C 写在 M 前面表示 M-C。

组合规则:
  1. 基本数字I、X、C中的任何一个,自身连用构成数目,或者放在大数的右边连用构成数目,都不能超过三个;放在大数的左边只能用一个。
  2. 不能把基本数字 V、L、D中的任何一个作为小数放在大数的左边采用相减的方法构成数目;放在大数的右边采用相加的方式构成数目,只能使用一个。
  3. V和X左边的小数字只能用I。
  4. L和C左边的小数字只能用X。
  5. D和M左边的小数字只能用C。

好了,资料也查了。开始整理思路了。当我查资料看到那张罗马数字对应的阿拉伯数字的表时,我就想到是不是该用哈希表来存储。我把罗马数字作为键,阿拉伯数字作为值存放到C#的Dictionary中。然后循环遍历罗马数字字符串,先将所有的罗马数字“无脑”相加,考虑到小数字在大数字前面表示的数是用大数字减去小数字,前面已经加过一次了,故在“无脑”相加后减去2倍的小数字。

代码如下:

public int RomanToInt(string s)  
    {
        Dictionary<char, int> dic = new Dictionary<char, int>();
        dic.Add('I', 1);
        dic.Add('V', 5);
        dic.Add('X', 10);
        dic.Add('L', 50);
        dic.Add('C', 100);
        dic.Add('D', 500);
        dic.Add('M', 1000);
        int sum = 0;
        for (int i = 0; i < s.Length; i++)
        {
            sum += dic[s[i]];
            if ((i < s.Length - 1) && (dic[s[i + 1]] > dic[s[i]]))    // i < s.Length - 1防止数组越界
            {
                sum -= 2 * dic[s[i]];
            }
        }
        return sum;
    }

当我提交完代码后,看到屏幕上的Accept内心毫无波动(笑),然而查看代码的运行时间傻眼了,才击败了36.05%的人。。。怎么可能,明明都用上了哈希表!!!好吧,我们来看下大神们是怎么做到。

大神解答:

//运行时间145ms
public int RomanToInt(string s) 
{
    int length = s.Length;
    int sum = 0;
    if(length < 2)
    {
        return Value(s[0]);
    }
    sum = Value(s[0]);
    int prev = sum;
    for(int i=1; i < length; i++)
    {
        char c1 = s[i];
        int val1 = Value(c1);
        if(val1 <= prev)
        {
            sum += val1;
            prev = val1;
        }
        else
        {
            sum = sum - prev;
            sum += (val1 - prev);
        }
    }
    return sum;
}

public int Value(char c)
{
    int val;
    if(c == 'C')
        val = 100;
    else if(c == 'L')
        val = 50;
    else if(c == 'X')
        val = 10;
    else if(c == 'V')
        val = 5;
    else if(c == 'D')
        val = 500;
    else if(c =='M')
        val = 1000;
    else
        val = 1;
    return val;
}
大神思路:

我本以为大神也会使用哈希表的,结果人家写了一个函数,函数内部是一个switch-case(果然大神的思路跟我们常人不同),用Value()函数来返回罗马数字对应的阿拉伯数字。在RomanToInt()中使用了一个变量pre来存储当前循环到的字符的上一个字符,然后判断当前的字符与前一个字符对应的罗马数字大小,大则加上,小则需要先减去前一个加上的数,再加上当前数与前一个数的差(比如MCMXCVI是1996,也就是按照逻辑遍历到C的处理,MC先M+C,但是遍历到第二个M的时候,顺序是CM,这个时候要减去这个C以及之前多加的C,也就是减去两个C。)

反思:

我在想是不是使用哈希表导致存储空间的增大而导致运行速度的降低。此题用switch-case应该更好一点,首先数据量不大,写起来也不见得比Dictionary要慢,其次,所占内存自然要比Dictionary要小的多。下次考虑问题的时候要全面,不要一上来就搞一些复杂的结构,简约而不简单!

你可能感兴趣的:(LeetCode,c#)