1

Possible Duplicate:
How to convert integer value to Roman numeral string?

first time asking a question here. I have a project on the horizon I've been working a bit on, and can't seem to find anything like it asked here or elsewhere. The goal is to accept an integer with no upper constraint, and convert that into a roman numeral. Since integers do in fact have an upper boundary, I have to convert it into a string before parsing it, using apostrophes to denote each set of three characters placed into a sub-string. I'm having trouble conceptualizing the loops that a)assign roman numerals to what they see based on their location b)count each set of three by displaying apostrophes.

So far I have:

for(int i=0;i<(int)input.length()/3;i++){
    temp=input.substr(i,3);
    for(int j = 0; j < (int)temp.length(); j++){
        if(j == 0){
            if(temp[j] == '9') cout<<"CM";
            else if(temp[j] >= '8') cout<<"DCCC";
            else if(temp[j] >= '7') cout<<"DCC";
            else if(temp[j] >= '6') cout<<"DC";
            else if(temp[j] >= '5') cout<<"D";
            else if(temp[j] == '4') cout<<"CD";
            else if(temp[j] == '3') cout<<"CCC";
            else if(temp[j] == '2') cout<<"CC";
            else if(temp[j] == '1') cout<<"C";  
        }
        else if(j == 1){
            if(temp[j] == '9') cout<<"XC";
            else if(temp[j] >= '8') cout<<"LXXX";
            else if(temp[j] >= '7') cout<<"LXX";
            else if(temp[j] >= '6') cout<<"LX";
            else if(temp[j] >= '5') cout<<"L";
            else if(temp[j] == '4') cout<<"XL";
            else if(temp[j] == '3') cout<<"XXX";
            else if(temp[j] == '2') cout<<"XX";
            else if(temp[j] == '1') cout<<"X";
        }
        else if(j ==2){
            if(temp[j] == '9') cout<<"IX";
            else if(temp[j] == '8') cout<<"VIII";
            else if(temp[j] == '7') cout<<"VII";
            else if(temp[j] == '6') cout<<"VI";
            else if(temp[j] >= '5') cout<<"V";
            else if(temp[j] == '4') cout<<"IV";
            else if(temp[j] == '3') cout<<"III";
            else if(temp[j] == '2') cout<<"II";
            else if(temp[j] == '1') cout<<"I";
        }
    }
}

The numerals display well enough on their own, but I'm having trouble figuring out how to tell the loop to start on the right, and work it's way left by threes, maintaining the actual place of the number in the input (e.g. 1234 should display 1 as I, not C. I also need to figure out the loop to write in the apostrophes.

Community
  • 1
  • 1
Travis
  • 11
  • 1
  • 4
  • 2
    probably this will helps you http://stackoverflow.com/questions/4986521/how-to-convert-integer-value-to-roman-numeral-string – Kaushal Sep 07 '12 at 18:16
  • 1
    @Kaushal this is C++ not C :) – Rapptz Sep 07 '12 at 18:33
  • Convert the character integer to `long`, then process that into Roman numerals. Any value larger than a `long` will be too big to print as Roman numerals anyway, given that `M` for `1000` is the largest standard numeral, so anything larger requires massive repeats. – Hot Licks Sep 07 '12 at 19:48
  • I think I did a poor job explaining the goal. The program should split the input into 3-digit sections, and display their value in that context, with a recursive amount of apostrophes denoting which 3-digit set is being displayed. So '1234' should display as I'CCXXXIV. Similarly, 123456789 should display as C''XX''III''CD'L'VI'DCCDCCCCM. I've nearly figured it out, I'll update the code soon. – Travis Sep 07 '12 at 19:55
  • That (rather strange) requirement does not require that you maintain decimal representation prior to conversion, only that you be able to count to 1000. – Hot Licks Sep 07 '12 at 20:28
  • @user1655431: Yeah, you did a pretty bad job of explaining. Cause the string for 1234556789 doesn't make a whole lot of sense. Did you mean C''XX''III''CD'L'VI'DCCLXXXIX? Really, why not just CXXIII'CDLVI'DCCLXXXIX (where the apostrophes say "we're starting a new segment; convert what you have and reset")? – cHao Sep 07 '12 at 21:41

2 Answers2

1

Simplest way i can think of to convert to roman numerals, is to check starting from the largest possible digit/combo and work down. Include combos, and check them from largest to smallest, so that say "XC" always gets checked before "L" and you don't have to worry about "LXXXX" and "LXL" and such.

// This code requires C++11 support.  Namely, initializer lists and type inference.
// If your compiler sucks, there's equivalents for the important stuff.  What really
// really matters is the order of the value=>digits mappings, and the iteration over
// them in the for loop.

#include <vector>
#include <string>
#include <utility>

std::string romanNumeralFor(int n, int markCount = 0) {
    typedef std::pair<int, std::string> valueMapping;
    static std::vector<valueMapping> importantNumbers = {
        {1000, "M"}, {900, "CM"}, {500, "D"}, {400, "CD"},
        {100,  "C"}, { 90, "XC"}, { 50, "L"}, { 40, "XL"},
        {10,   "X"}, {  9, "IX"}, {  5, "V"}, {  4, "IV"},
        {1,    "I"},
    };

    std::string result;
    bool needMark = false;
    std::string marks(markCount, '\'');
    for (auto mapping : importantNumbers) {
        int value = mapping.first;
        std::string &digits = mapping.second;
        while (n >= value) {
            result += digits;
            n -= value;
            needMark = true;
        }
        if ((value == 1000 || value == 100 || value == 10 || value == 1) && needMark) {
            result += marks;
            needMark = false;
        }
    }
    return result;
}

As for converting a string to a number:

// in C++11
int n = std::stoi(str);

// in C++03
std::istringstream iss(str);
int n;
iss >> n;

So, split your string up into three-digit chunks (starting from the end!), and pass them in with appropriate mark counts.

cHao
  • 84,970
  • 20
  • 145
  • 172
0

Not the best solution but it works.

#include <iostream>
#include <map>
#include <algorithm>
#include <string>

void replaceAll(std::string& str, const std::string& from, const std::string& to) {
    if(from.empty())
        return;
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos) {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length();
    }
}

int romanNumeralToInt(std::string s) {
    std::map<char,int> romanNum = {
        {'I',1}, {'V',5}, {'X',10}, {'L',50}, {'C',100},
        {'D',500}, {'M',1000}
    };
    //{"IIIII",5}, {"VV",10}, {"XXXXX",50}, {"LL",100}, {"CCCCC",500}, {"DD",1000}
    int g = 0;
    std::sort(s.begin(),s.end());
    if(s.find("IIIII") != std::string::npos)
        replaceAll(s,"IIIII","V");
    if(s.find("VV") != std::string::npos)
        replaceAll(s,"VV","X");
    if(s.find("XXXXX") != std::string::npos)
        replaceAll(s,"XXXXX","L");
    if(s.find("LL") != std::string::npos)
        replaceAll(s,"LL","C");

    for(auto& i : s) {
        if(romanNum[i] != 0)
            g += romanNum[i];
    }
    return g;
}

int main() {
    std::string st = "XXXXXIIIIIVVVXLLD";
    int a = romanNumeralToInt(st);
    std::cout << a;
}

Prints out 680.

Rapptz
  • 20,807
  • 5
  • 72
  • 86
  • And if i'm reading the code right, "XL" gets you 60, rather than 40. Either way, the question was asking about the other way around. – cHao Sep 07 '12 at 19:30
  • @cHao [There are no rules governing the order of Roman Numerals.](http://en.wikipedia.org/wiki/Roman_numerals#Reading_Roman_numerals) hence why I ignored them. I know it isn't the best solution though. – Rapptz Sep 07 '12 at 19:34