LeetCodeOJ刷题之12【Integer to Roman】

Integer to Roman

Given an integer, convert it to a roman numeral.
Input is guaranteed to be within the range from 1 to 3999.
意思就是:
给出一个整数 num( 0<=num<=3999),返回其对应的罗马数字表示;
罗马数字有:

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

Solutions

  • 1 Integer to Roman -- 143ms

    • 这个方式不知道为什么很慢,是我第一个想出的几乎相当于暴力的方法。
    class Solution {
    public:
        string intToRoman(int num) {
            if(num<=0||num>3999)return string();
            map<int,string> one,two,three,four;  //这里使用的是 Map
            one[0]="";      two[0]="";      three[0]=""; 
            one[1]="I";     two[1]="X";     three[1]="C";       four[1]="M";
            one[2]="II";    two[2]="XX";    three[2]="CC";      four[2]="MM";
            one[3]="III";   two[3]="XXX";   three[3]="CCC";     four[3]="MMM";
            one[4]="IV";    two[4]="XL";    three[4]="CD";  
            one[5]="V";     two[5]="L";     three[5]="D";  
            one[6]="VI";    two[6]="LX";    three[6]="DC";  
            one[7]="VII";   two[7]="LXX";   three[7]="DCC";  
            one[8]="VIII";  two[8]="LXXX";  three[8]="DCCC";  
            one[9]="IX";    two[9]="XC";    three[9]="CM";
            
            int mod=0,idx=1;
            string rs="",tmp="";
            while(num>0){
                mod=num%10;
                switch(idx){
                    case 1:{
                        tmp=one[mod];
                        rs=tmp+rs;
                        break;
                    }
                    case 2:{
                        tmp=two[mod];
                        rs=tmp+rs;
                        break;
                    }
                    case 3:{
                        tmp=three[mod];
                        rs=tmp+rs;
                        break;
                    }
                    case 4:{
                        tmp=four[mod];
                        rs=tmp+rs;
                        break;
                    }
                }
                num=num/10;
                ++idx;
            }
            return rs;
        }
    };
    

    后来经过了测试,发现貌似主要原因在于使用了 map,所以在第二次测试中将 map 改为了 string 数组

  • 2 Integer to Roman -- 68ms

    • 果然,改进后,运行时间从原来的 143ms 变成了 68ms ,这是第一个改进:
    //原来的 map
    map<int,string> one,two,three,four;  //这里使用的是 Map
    //改进后
    string* one=new string[10],*two=new string[10],*three=new string[10],*four=new string[4];  
    
  • 3 Integer to Roman -- 54ms

    • 方法1和2中,使用了很大的空间来存储全部的罗马特殊数字,增加了空间复杂性,所以我就想,能不能不存储这些特殊数字,只存储最基本的罗马基数数字 IVXLCDM ,然后根据当前数字进行判断,生成对应的罗马数字。下面是这一思想的代码:
    class Solution {
    public:
        string intToRoman(int num) {
            if(num<=0||num>3999)return string();
            string roman="IVXLCDM";  //存储基数
            string rs="",tmp="";
            int ndx=0,mod=0;  // ndx 用来记录当前基数(即第n位对应的最小罗马数字坐标)
            while(num>0){
                mod=num%10; //求余数
                if(ndx==6){ //千位只有 M
                    rs=string(mod,'M')+rs;
                    break;
                }
                if(mod==0){
                    tmp="";
                }else if(mod<4){
                    tmp=string(mod,roman[ndx]);
                }else if(mod==4){
                    tmp=string(1,roman[ndx])+string(1,roman[ndx+1]);
                }else if(mod<9){
                    tmp=string(1,roman[ndx+1])+string(mod-5,roman[ndx]);
                }else if(mod==9){
                    tmp=string(1,roman[ndx])+string(1,roman[ndx+2]);
                }
                rs=tmp+rs;
                ndx+=2; //基数向前进2个
                num/=10;
            }
            return rs;
        }
    };
    
    • 这一思想中,有两点是需要考虑的:
      1. 千位只有 M
      2. 数字4 , 9很特殊;
    • 对于数字 4 来说,如果当前基数坐标为 ndx ,则其对应的罗马数字为:
    string(1,roman[ndx])+string(1,roman[ndx+1]);
    

    比如当前位为十位,则 ndx=2; roman[ndx]='X'; roman[ndx+1]='L' ; 对应罗马数字为: XL

    • 对于数字 9 来说,对应罗马数字为:
    string(1,roman[ndx])+string(1,roman[ndx+2]);
    

    比如当前位为十位,则 ndx=2; roman[ndx]='X'; roman[ndx+2]='C' ; 对应罗马数字为: XC

    • 小于 4 的数字 mod,则为 mod 个连续基数;
    • 大于 4 小于 9 的数字 mod , 则为 一个 roman[ndx+1]mode-5 个连续基数;
    • 同时,这个版本中,我发觉还是可以改进一下的,对于代码的改进,所以出现了方法4
  • 4 Integer to Roman -- 48ms

    • 对于代码的进行,方法3中会产生大量的string临时变量,因此这里尽量的使用了迭代器和 string 的insert方法进行处理。代码运行效率得以再次提高:48ms
    class Solution {
    public:
        string intToRoman(int num) {
            if(num<=0||num>3999)return string();
            char roman[]="IVXLCDM";
            string rs="";
            int ndx=0,mod=0;
            while(num>0){
                mod=num%10;
                if(ndx==6){
                    rs=string(mod,'M')+rs;
                    break;
                }
                if(mod<4){
                    rs=string(mod,roman[ndx])+rs;
                }else if(mod==4){
                    rs.insert(rs.begin(),roman[ndx+1]);
                    rs.insert(rs.begin(),roman[ndx]);
                    // rs=string(1,roman[ndx])+string(1,roman[ndx+1])+rs;
                }else if(mod<9){
                    // rs=string(1,roman[ndx+1])+string(mod-5,roman[ndx])+rs;
                    rs=string(mod-5,roman[ndx])+rs;
                    rs.insert(rs.begin(),roman[ndx+1]);
                }else if(mod==9){
                    rs.insert(rs.begin(),roman[ndx+2]);
                    rs.insert(rs.begin(),roman[ndx]);
                    // rs=string(1,roman[ndx])+string(1,roman[ndx+2])+rs;
                }
                ndx+=2;
                num/=10;
            }
            return rs;
        }
    };
    
  • 5 Integer to Roman -- 57ms

    • 这是LeetCode上网友的答案,感觉跟简洁,同时算法也很容易理解,和这里第一种的思想很像:
    class Solution {
    public:
        string intToRoman(int num) {
            string M[] = {"", "M", "MM", "MMM"};
            string C[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
            string X[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
            string I[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
            return M[num/1000] + C[(num%1000)/100] + X[(num%100)/10] + I[num%10];
        }
    };
    
    • 但这里有一个问题,就是这种方法的思想和第二种很像,但是运行时间却更少,于是我将第二种的方法又进行了改进,我感觉差异应该是出现在:第二种方法在赋值时是先定义后赋值,且使用了 new 操作符,这里没有!所以有了下一个简单的改进:
    • 方法二的四个字符串数组,使用方法 5 的方式定义赋值。运行后时间为 58ms,果然这个是原因!看来以后对于内容不是很多的字符数组应该优先不使用 new ,这里还要注意的是,我在第二种方法里 new 的那些个字符数组~~~,忘了一个很重要的操作:delete 。😱
  • 6 Integer to Roman -- 49ms

    • 这个是网友的答案:
    class Solution {
    public:
        string intToRoman(int num) {
            // IMPORTANT: Please reset any member data you declared, as
            // the same Solution instance will be reused for each test case.    
            string result = "";
            int base[] = {1000,500,100,50,10,5,1,0};
            char baseC[] = {'M','D','C','L','X','V','I'};
            int basen = 0;
            while(num) {
                if(basen%2 == 0 && num/base[basen] == 4) {
                    result += baseC[basen];
                    result += baseC[basen-1];
                    num -= base[basen] * 4;
                } else if(num >= base[basen]) {
                    result += baseC[basen];
                    num -= base[basen];
                } else if(basen%2 == 0 && num / base[basen+2] == 9) {
                    result += baseC[basen+2];
                    result += baseC[basen];
                    num -= base[basen+2]*9;
                } else {
                    basen++;
                }
            }
            return result;
        }
    };
    
    • 代码呢,一下子看思路不是很清楚,感觉思想好像有点复杂。
    • 网友给出的思路是这样的:
      1. 如果 num 是 x 的 4 的倍数(其中,x=1000,100,10,1),则增加一个 x 对应的罗马数字和 5x 对应的对应的罗马数字;
        如:400是100的4倍,则增加一个100对应的罗马数字 C ,和5*100=500对应的罗马数字 D,则 400=CD ;
      2. 如果 num > x ,则添加一个 x 对应的罗马数字;
        如: 6 > 5 ,则添加一个 5 对应的罗马数字 V ;
      3. 如果 num < x (x=1000,100,10,1),但是 num 是 x/10 的 9 倍,则添加一个 x/10 对应的数字和 x 对应的数字。
        如: 90 < 100 ,但 90==9*(100/10) ,则添加一个 (100/10)=10 对应的罗马数字 X 和一个 100 对应的罗马数字 C ,则 90=XC;
    • 个人觉得这个算法有点复杂!不过还是挺高效的。这里学习一下思想。

附录

同系列:LeetCodesOJhttp://www.cnblogs.com/lomper/tag/LeetCodesOJ/

posted @ 2015-03-13 22:57  九二  阅读(728)  评论(0编辑  收藏  举报