1. 程式人生 > >13. Roman to Integer題目和答案詳解

13. Roman to Integer題目和答案詳解

1 題目簡述

  Given aroman numeral, convert it to an integer.

  Input isguaranteed to be within the range from 1 to 3999.

  給定一個羅馬數字,將其轉換為整數。

  輸入保證在1到3999的範圍內。

2 答案詳解

(1) 規則分析

  首先,要知道羅馬數字有7個:I(1),V(5),X(10),L(50),C(100),D(500),M(1000)。然後要知道羅馬計數的一些規則,先來看這樣的例子:阿拉伯數字1~11用羅馬數字表示為:

I、II、III、IV、V、VI、VII、VIII、IX、X、XI

以此為例,說明四點一般規則:

  1)只有I、X、C可作為餘下數字的字首,例如VM、LC則為非法。

  2)對於I、X、C(1開頭的數字,M除外)來說,單次使用量最多不能4個,這是因為IIII是非法的,IV才是正確合法的,還有XXXX是非法的,XL才是正確合法的。

  3)對於V、L、D(5開頭的數字)來說,單次使用量最多為1個,這是因為VV不能表示10,而X才表示10。

  4)此外,還有一些非法數字的情況,例如:XIIX,它符合前3點規則,將其分開為XI和IX,即為11和9,相加得20,但XX才表示20,故XIIX是非法的。同理,DCCCD也是非法的。ps:一個阿拉伯數字只有一種羅馬數字表示法。

  注意:羅馬數字表示法還有一種規則是在數字上方加上橫線,表示其1000倍的數,但對於本次題目,上述四項規則已經足以解決。

(2) 解決思想

  根據所述規則,由於採用C++來編寫程式,而C++相對於C語言特性中重要的一點是面向物件程式設計(OOP),故本次設計採用類的方法實現,該類表明所儲存的資料型別為vector<char>的容器。設計總體分兩步進行:

  1)初步判斷輸入的羅馬數字是否合法。首先,若輸入的字母中含有7個羅馬數字之外的數字,則為非法;然後,若是違反了前三項規則,則為非法。依次將合法的數字放入容器中。在初步判斷正確的情況下,仍然無法避免會違反第四項規則。對於是否符合第四項規則的合法性,則留給將初步合法的數字轉化為阿拉伯數字時判斷。

  2)從頭開始遍歷容器,從高位到低位,依次將每一項加入到結果中。對於有字首的項,有且只有一個字首數字(例如IV、IX),故I、X、C若是遇到比其大的數字則求其組合代表的阿拉伯數字值(例如求IV為4,IX為9);對於有後綴的項則不用將其組合,因為VIII為5+1+1+1的值;對於第四項規則的處理,可以看到加入到結果中的項是從高位加到低位的,所以若在累加的過程中,遇到前一個加入的項小於後一個加入的項時,則為非法(例如XIIX:9+1+10,1比10小,所以該數字非法),這是因為從高位到低位的項只可能越來越小;最後,對於保證羅馬數字的值在1~3999範圍,則由最後的結果判斷。

(3) 設計程式

   所設計的程式採用類模板,雖然本次模板設計沒有體現其延伸性和程式碼重用性,但也是出於此目的所養成的習慣。程式如下:
#include <iostream>
#include <vector>
#include <algorithm>

using std::cin;
using std::cerr;
using std::cout;
using std::endl;
using std::vector;

void display(char& c)
{
    cout << c;
}

template<class T>
class RomanToInteger
{
private:
    vector<T> romans_;
public:
    RomanToInteger(const vector<T>& romans = vector<T>(0)):romans_(romans) {}
    int result();
private:
    void judge();
    void exit_() {
        cerr << "Wrong Input" << endl;
        exit(EXIT_FAILURE);
    }
    void out_range() const {
        cerr << "Out of Range" << endl;
        exit(EXIT_FAILURE);
    }
};

template<class T>
void RomanToInteger<T>::judge()
{
    typename vector<T>::iterator it;
    int count;
    int left;
    for(it = romans_.begin(); it != romans_.end(); it++) {
        left = distance(it,romans_.end());
        if(left == 0) {
            exit_();
        } else if(*it == 'C' or *it == 'X' or *it == 'I') {
            if(left > 3 and *it == *(it + 1) and *it == *(it + 2) and *it == *(it + 3)) {
                exit_();
            } else if(left > 1 and *it == 'I' and (*(it + 1) == 'L' or *(it + 1) == 'C'
                                                   or *(it + 1) == 'D' or *(it + 1) == 'M')) {
                exit_();
            } else if(left > 1 and *it == 'X' and (*(it + 1) == 'D' or *(it + 1) == 'M')) {
                exit_();
            }
        } else if(*it == 'D' or *it == 'L' or *it == 'V') {
            if(left > 1 and *it == *(it + 1)) {
                exit_();
            } else if(left > 1 and *it == 'V' and (*(it + 1) == 'X' or *(it + 1) == 'L'
                                                   or *(it + 1) == 'C' or *(it + 1) == 'D' or *(it + 1) == 'M')) {
                exit_();
            } else if(left > 1 and *it == 'L' and (*(it + 1) == 'C' or *(it + 1) == 'D'
                                                   or *(it + 1) == 'M')) {
                exit_();
            } else if(left > 1 and *it == 'D'and *(it + 1) == 'M') {
                exit_();
            }
        }
    }
    cout << "The Number is:";
    for_each(romans_.begin(),romans_.end(),display);
    cout << endl;
    return;
}

template<class T>
int RomanToInteger<T>::result()
{
    judge();
    int res(0);
    int add(0);
    int add_before(0);
    typename vector<T>::iterator it;
    for(it = romans_.begin(); it != romans_.end(); it++) {
        if(*it == 'I') {
            if(*(it + 1) ==  'X') {
                add = 9;
                it++;
            } else if(*(it + 1) == 'V') {
                add = 4;
                it++;
            } else {
                add = 1;
            }
        } else if(*it == 'V') {
            add = 5;
        } else if(*it == 'X') {
            if(*(it + 1) == 'C') {
                add = 90;
                it++;
            } else if(*(it + 1) == 'L') {
                add = 40;
                it++;
            } else {
                add = 10;
            }
        } else if(*it == 'L') {
            add = 50;
        } else if(*it == 'C') {
            if(*(it + 1) == 'M') {
                add = 900;
                it++;
            } else if(*(it + 1) == 'M') {
                add = 400;
                it++;
            } else {
                add = 100;
            }
        } else if(*it == 'D') {
            add = 500;
        } else if(*it == 'M') {
            add = 1000;
        }
        if(!add_before) {
            add_before = add;
        } else if(add_before < add) {
            exit_();
        } else {
            add_before = add;
        }
        res += add;
        if(res > 3999) {
            out_range();
        }
    }
    if(res == 0) {
        out_range();
    }
    return res;
}

int main()
{
    vector<char> vec;
    char ch;
    int res;
    cout << "Please Iuput Number of Roman:";
    while((ch = cin.get()) != '\n') {
        if(ch != 'M'and ch != 'D'and ch != 'C'and ch != 'L'and ch != 'X'and ch != 'V' and ch != 'I') {
            cerr << "Wrong Input" << endl;
            exit(EXIT_FAILURE);
        } else {
            vec.push_back(ch);
        }
    }
    RomanToInteger<char> rti(vec);
    res = rti.result();
    cout << "Integer Number:" << res << endl;
}
本次測試了4種不同輸入情況的執行結果,執行結果為:

Please Iuput Number of Roman:ASDCF
Wrong Input

Please Iuput Number of Roman:VX    
Wrong Input

Please Iuput Number of Roman:XIIX
The Number is:XIIX
Wrong Input

Please Iuput Number of Roman:MDCLXVI
The Number is:MDCLXVI
Integer Number:1666