roman_numerals from bluefruit competition. http://www.bluefruit.co.uk/careers/win/
This commit is contained in:
271
puzzles/bluefruit/roman_numerals.cpp
Normal file
271
puzzles/bluefruit/roman_numerals.cpp
Normal file
@@ -0,0 +1,271 @@
|
||||
/*
|
||||
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;
|
||||
}}
|
||||
|
||||
Reference in New Issue
Block a user