272 lines
7.6 KiB
C++
272 lines
7.6 KiB
C++
/*
|
|
VIM: let g:lcppflags="-std=c++11 -O2 -pthread"
|
|
VIM: let g:wcppflags="/O2 /EHsc /DWIN32"
|
|
VIM: let g:argv=""
|
|
|
|
ID 971852
|
|
*/
|
|
#include <iostream>
|
|
#include <exception>
|
|
|
|
/*
|
|
Given a Roman number as a string (eg "XX") determine
|
|
its integer value (eg 20).
|
|
|
|
You cannot write numerals like IM for 999.
|
|
Wikipedia states "Modern Roman numerals are written by
|
|
expressing each digit separately starting with the
|
|
leftmost digit and skipping any digit with a value of zero."
|
|
|
|
Examples:
|
|
|
|
"I" -> 1 | "X" -> 10 | "C" -> 100 | "M" -> 1000
|
|
"II" -> 2 | "XX" -> 20 | "CC" -> 200 | "MM" -> 2000
|
|
"III" -> 3 | "XXX" -> 30 | "CCC" -> 300 | "MMM" -> 3000
|
|
"IV" -> 4 | "XL" -> 40 | "CD" -> 400 | "MMMM" -> 4000
|
|
"V" -> 5 | "L" -> 50 | "D" -> 500 |
|
|
"VI" -> 6 | "LX" -> 60 | "DC" -> 600 |
|
|
"VII" -> 7 | "LXX" -> 70 | "DCC" -> 700 |
|
|
"VIII" -> 8 | "LXXX" -> 80 | "DCCC" -> 800 |
|
|
"IX" -> 9 | "XC" -> 90 | "CM" -> 900 |
|
|
|
|
"MCMXC" -> 1990 ("M" -> 1000 + "CM" -> 900 + "XC" -> 90)
|
|
"MMVIII" -> 2008 ("MM" -> 2000 + "VIII" -> 8)
|
|
"XCIX" -> 99 ("XC" -> 90 + "IX" -> 9)
|
|
"XLVII" -> 47 ("XL" -> 40 + "VII" -> 7)
|
|
*/
|
|
int roman2num( const char * roman_num ) {
|
|
//
|
|
// Possible digits.
|
|
//
|
|
enum digits { D_I = 0, D_V, D_X, D_L, D_C, D_D, D_M, D_ERR };
|
|
//
|
|
// The states of parser. The names of states are quite intuitive. The
|
|
// vI, lX, dC are the states corresponding to patterns VI+, LX+, DC+
|
|
// when the first I,X,C letters are seen.
|
|
//
|
|
enum states {
|
|
S = 0, /* START state. */
|
|
I, vI, II, III, IV, V, IX,
|
|
X, lX, XX, XXX, XL, L, XC,
|
|
C, dC, CC, CCC, CD, D, CM,
|
|
M, MM, MMM, MMMM,
|
|
E /* ERROR state */
|
|
};
|
|
//
|
|
// This table defines state transition for each character seen.
|
|
//
|
|
static const char state_table[][8] = {
|
|
/* I, V, X, L, C, D, M, D_ERR */
|
|
{ I, V, X, L, C, D, M, E}, //S
|
|
|
|
{ II, IV, IX, E, E, E, E, E}, //I
|
|
{ II, E, E, E, E, E, E, E}, //vI
|
|
{III, E, E, E, E, E, E, E}, //II
|
|
{ E, E, E, E, E, E, E, E}, //III
|
|
{ E, E, E, E, E, E, E, E}, //IV
|
|
{ vI, E, E, E, E, E, E, E}, //V
|
|
{ E, E, E, E, E, E, E, E}, //IX
|
|
|
|
{ I, V, XX, XL, XC, E, E, E}, //X
|
|
{ I, V, XX, E, E, E, E, E}, //lX
|
|
{ I, V,XXX, E, E, E, E, E}, //XX
|
|
{ I, V, E, E, E, E, E, E}, //XXX
|
|
{ I, V, E, E, E, E, E, E}, //XL
|
|
{ I, V, lX, E, E, E, E, E}, //L
|
|
{ I, V, E, E, E, E, E, E}, //XC
|
|
|
|
{ I, V, X, L, CC, CD, CM, E}, //C
|
|
{ I, V, X, L, CC, E, E, E}, //dC
|
|
{ I, V, X, L,CCC, E, E, E}, //CC
|
|
{ I, V, X, L, E, E, E, E}, //CCC
|
|
{ I, V, X, L, E, E, E, E}, //CD
|
|
{ I, V, X, L, dC, E, E, E}, //D
|
|
{ I, V, X, L, E, E, E, E}, //CM
|
|
|
|
{ I, V, X, L, C, D, MM, E}, //M
|
|
{ I, V, X, L, C, D, MMM, E}, //MM
|
|
{ I, V, X, L, C, D, MMMM, E}, //MMM
|
|
{ I, V, X, L, C, D, E, E}, //MMMM
|
|
|
|
{ E, E, E, E, E, E, E, E} //E
|
|
};
|
|
//
|
|
// Each state transition causes an increment of the number with
|
|
// certain value defined in this table.
|
|
// A state transition happens for each character we read, hence when
|
|
// defining the increment value of complex states such as II,III,IV,...
|
|
// please take into consideration what amount increment we already have.
|
|
// For example the state XL is defined as 30 since we already added 10 when
|
|
// we see X.
|
|
//
|
|
static const short increment[] = {
|
|
//S This is here to preserve alignment only.
|
|
0,
|
|
// I, vI, II, III, IV, V, IX
|
|
1, 1, 1, 1, 3, 5, 8,
|
|
// X, lX, XX, XXX, XL, L, XC
|
|
10, 10, 10, 10, 30, 50, 80,
|
|
// C, dC, CC, CCC, CD, D, CM,
|
|
100, 100, 100, 100, 300, 500, 800,
|
|
// M, MM, MMM, MMMM */
|
|
1000, 1000, 1000, 1000,
|
|
//E This is the error condition. The value doesn't really matter.
|
|
0
|
|
};
|
|
//
|
|
// Ideally this had to be a table based lookup.
|
|
//
|
|
auto chr2digit = []( char ch ) {
|
|
switch (ch) {
|
|
case 'I' : return D_I;
|
|
case 'V' : return D_V;
|
|
case 'X' : return D_X;
|
|
case 'L' : return D_L;
|
|
case 'C' : return D_C;
|
|
case 'D' : return D_D;
|
|
case 'M' : return D_M;
|
|
default : return D_ERR;
|
|
}
|
|
};
|
|
//
|
|
// Here the algorithm begins.
|
|
//
|
|
if ( !roman_num )
|
|
return -1;
|
|
|
|
int num = 0;
|
|
int state = S;
|
|
for ( const char * e = roman_num; *e && state < E; ++e ) {
|
|
state = state_table[state][chr2digit(*e)];
|
|
num += increment[state];
|
|
}
|
|
if ( state >= E )
|
|
return -1;
|
|
return num;
|
|
}
|
|
|
|
void ASSERT_EQ( int golden, int ret ) {
|
|
if ( ret != golden )
|
|
std::cout << "FAIL: Expected " << golden << " got " << ret << std::endl;
|
|
}
|
|
|
|
int main ( void )
|
|
{try{
|
|
ASSERT_EQ(0, roman2num(""));
|
|
ASSERT_EQ(1, roman2num("I"));
|
|
ASSERT_EQ(2, roman2num("II"));
|
|
ASSERT_EQ(3, roman2num("III"));
|
|
ASSERT_EQ(4, roman2num("IV"));
|
|
ASSERT_EQ(5, roman2num("V"));
|
|
ASSERT_EQ(6, roman2num("VI"));
|
|
ASSERT_EQ(7, roman2num("VII"));
|
|
ASSERT_EQ(8, roman2num("VIII"));
|
|
ASSERT_EQ(9, roman2num("IX"));
|
|
|
|
ASSERT_EQ(10, roman2num("X"));
|
|
ASSERT_EQ(20, roman2num("XX"));
|
|
ASSERT_EQ(30, roman2num("XXX"));
|
|
ASSERT_EQ(40, roman2num("XL"));
|
|
ASSERT_EQ(50, roman2num("L"));
|
|
ASSERT_EQ(60, roman2num("LX"));
|
|
ASSERT_EQ(70, roman2num("LXX"));
|
|
ASSERT_EQ(80, roman2num("LXXX"));
|
|
ASSERT_EQ(90, roman2num("XC"));
|
|
|
|
ASSERT_EQ(100, roman2num("C"));
|
|
ASSERT_EQ(200, roman2num("CC"));
|
|
ASSERT_EQ(300, roman2num("CCC"));
|
|
ASSERT_EQ(400, roman2num("CD"));
|
|
ASSERT_EQ(500, roman2num("D"));
|
|
ASSERT_EQ(600, roman2num("DC"));
|
|
ASSERT_EQ(700, roman2num("DCC"));
|
|
ASSERT_EQ(800, roman2num("DCCC"));
|
|
ASSERT_EQ(900, roman2num("CM"));
|
|
|
|
ASSERT_EQ(1000, roman2num("M"));
|
|
ASSERT_EQ(2000, roman2num("MM"));
|
|
ASSERT_EQ(3000, roman2num("MMM"));
|
|
ASSERT_EQ(4000, roman2num("MMMM"));
|
|
|
|
ASSERT_EQ(1990, roman2num("MCMXC"));
|
|
ASSERT_EQ(2008, roman2num("MMVIII"));
|
|
ASSERT_EQ(91, roman2num("XCI"));
|
|
ASSERT_EQ(99, roman2num("XCIX"));
|
|
ASSERT_EQ(47, roman2num("XLVII"));
|
|
ASSERT_EQ(3888, roman2num("MMMDCCCLXXXVIII"));
|
|
ASSERT_EQ(4888, roman2num("MMMMDCCCLXXXVIII"));
|
|
ASSERT_EQ(4999, roman2num("MMMMCMXCIX"));
|
|
|
|
static const char * num [] = {
|
|
"","I","II","III","IV","V","VI","VII","VIII","IX",
|
|
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
|
|
"","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
|
|
"","M","MM","MMM","MMMM"
|
|
};
|
|
for ( int i = 0; i < 5000; ++i ) {
|
|
int d4 = i/1000;
|
|
int d3 = i%1000/100;
|
|
int d2 = i%100/10;
|
|
int d1 = i%10;
|
|
|
|
std::string test = num[30+d4];
|
|
test +=num[20+d3];
|
|
test +=num[10+d2];
|
|
test +=num[d1];
|
|
|
|
ASSERT_EQ(i, roman2num(test.c_str()));
|
|
}
|
|
|
|
//
|
|
// Negative cases.
|
|
//
|
|
ASSERT_EQ(-1, roman2num(0));
|
|
ASSERT_EQ(-1, roman2num("IXLVII"));
|
|
ASSERT_EQ(-1, roman2num("IIII"));
|
|
ASSERT_EQ(-1, roman2num("VV"));
|
|
ASSERT_EQ(-1, roman2num("XXXX"));
|
|
ASSERT_EQ(-1, roman2num("LL"));
|
|
ASSERT_EQ(-1, roman2num("CCCC"));
|
|
ASSERT_EQ(-1, roman2num("DD"));
|
|
ASSERT_EQ(-1, roman2num("MMMMM"));
|
|
ASSERT_EQ(-1, roman2num("IIV"));
|
|
ASSERT_EQ(-1, roman2num("IVI"));
|
|
ASSERT_EQ(-1, roman2num("IIX"));
|
|
ASSERT_EQ(-1, roman2num("IXI"));
|
|
ASSERT_EQ(-1, roman2num("XXC"));
|
|
ASSERT_EQ(-1, roman2num("XCX"));
|
|
ASSERT_EQ(-1, roman2num("XXL"));
|
|
ASSERT_EQ(-1, roman2num("XLX"));
|
|
ASSERT_EQ(-1, roman2num("CCM"));
|
|
ASSERT_EQ(-1, roman2num("CMC"));
|
|
ASSERT_EQ(-1, roman2num("CCD"));
|
|
ASSERT_EQ(-1, roman2num("CDC"));
|
|
|
|
ASSERT_EQ(-1, roman2num("c"));
|
|
ASSERT_EQ(-1, roman2num("l"));
|
|
ASSERT_EQ(-1, roman2num("x"));
|
|
ASSERT_EQ(-1, roman2num("v"));
|
|
ASSERT_EQ(-1, roman2num("v"));
|
|
ASSERT_EQ(-1, roman2num("v"));
|
|
ASSERT_EQ(-1, roman2num("v"));
|
|
ASSERT_EQ(-1, roman2num("1"));
|
|
ASSERT_EQ(-1, roman2num("2"));
|
|
ASSERT_EQ(-1, roman2num("a"));
|
|
ASSERT_EQ(-1, roman2num("."));
|
|
|
|
return 0;
|
|
}
|
|
catch ( const std::exception& e )
|
|
{
|
|
std::cerr << std::endl
|
|
<< "std::exception(\"" << e.what() << "\")." << std::endl;
|
|
return 2;
|
|
}
|
|
catch ( ... )
|
|
{
|
|
std::cerr << std::endl
|
|
<< "unknown exception." << std::endl;
|
|
return 1;
|
|
}}
|
|
|