Leetcode 罗马数字的两道题目

在Leetcode上有两道题目是有关罗马数字的,一道是把罗马数字转换成阿拉伯数字,另一道是把阿拉伯数字转换成罗马数字。

具体来说是Leetcode 12题Integer to Roman和13题Roman to Integer

具体的规则可见https://en.wikipedia.org/wiki/Roman_numerals或者https://baike.baidu.com/item/%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97/772296?fr=aladdin。

简单来说,是遵守左减右加的计算规则,即如果相邻的两个字母(罗马数字),如果左边的数字小于右边的数字,这两个字母的结果就是右边的数字减去左边的数字。如果是大于等于,则进行相加。

比较好的做题顺序是13->12(由易到难)。

那我们首先来做第13题。

主要的思路是

1. 根据基本规则建立哈希表。

2. 遍历罗马数字对应的字符串,将当前的字母对应的数字和前一个字母对应的数字进行比较。按照左减右加的规则进行运算。

class Solution {
public:
    int romanToInt(string s) {
        if (s.size() == 0) {
            return 0;
        }
        
        int digits[] = {1, 5, 10, 50, 100, 500, 1000};
        char symbols[] = "IVXLCDM";
        
        unordered_map<char,int> hashmap;
        
        for(int i=0; i<sizeof(digits) / sizeof(int); ++i) {
            hashmap[symbols[i]] = digits[i];
        }
        
        int num = 0;
        int prev = 1000;
        int cur = 0;
        
        for(int i=0; i<s.size(); ++i) {
            cur = hashmap[s[i]];
            
            if (prev < cur) {
                num = num + cur - 2 * prev;
            }
            
            else {
                num = num + cur;
            }
            
            prev = cur;
        }
        
        return num;
    }

};

接着我们来看第12题。

这道题做起来就没有那么容易了。那我们来看看一些特殊的数据,Ⅳ-4,Ⅸ-9,XL-40,XLI-41,XC-90,XCIII-93、XCV-95、XCVIII-98、XCIX-99等。我们发现无论是4和9这两个单独的数,还是处于[40, 50)或者[90, 100)是两个区间。都是需要先加后减,如果是区间,则加上个位数,最终得到正确的答案。

根据以上思路,代码如下所示:

class Solution {
public:
     
    string getNumByFourOrNine(int &num, int base, int sub, unordered_map<int, char> &hashmap) {
        string res;

        if (num + sub < base) {
            return "";
        }

        while (num >= base) {
            res = res + hashmap[base];
            num -= base;
        }

        if (num + sub >= base) {
            res = res + hashmap[sub] + hashmap[base];
            num = num + sub - base;
        }

        return res;
    }
    
    string intToRoman(int num) {
        int bases[7] = {1, 5, 10, 50, 100, 500, 1000};
        char digits[8] = "IVXLCDM";
        string res;
        unordered_map<int,char> hashmap;
        
        for (int i=0; i<sizeof(bases)/sizeof(int); ++i) {
            hashmap[bases[i]] = digits[i]; 
        }
        
        for(int i = sizeof(bases) /sizeof(int) - 1; i >= 1; i--) {
            if (i % 2 == 0) {
                res += getNumByFourOrNine(num, bases[i], bases[i-2], hashmap);
            }

            else {
                res += getNumByFourOrNine(num, bases[i], bases[i-1], hashmap);
            }
        }
        

        while(num >= 1 && num<= 4) {
            res += "I";
            --num;
        }

        return res;

    }
};

上面的代码实现了功能,但还不够简化。主要是因为对于包含4和9的数,是现算的,而如果事先有表直接查询,则可减少运行时间,可参考如下代码:

class Solution {
public:
    string intToRoman(int num) {
        string str;  
        string symbol[]={"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};  
        int value[]= {1000, 900, 500, 400, 100, 90,  50, 40, 10, 9, 5, 4, 1};
        
        for(int i=0;num>0;++i)
        {
            while(num-value[i]>=0)
            {
                num-=value[i];
                str+=symbol[i];
            }
        }
        return str;
    }
};

是不是很简洁啊

 

posted @ 2018-08-21 00:24  ly-bnu  阅读(279)  评论(0编辑  收藏  举报