I have managed to get my code to convert most Roman numerals to its appropriate decimal value. But it doesn't work for some exceptional cases. Example : XCIX = 99 but my code prints 109.
Here is my code.
public static int romanConvert(String roman)
{
int decimal = 0;
String romanNumeral = roman.toUpperCase();
for(int x = 0;x<romanNumeral.length();x++)
{
char convertToDecimal = roman.charAt(x);
switch (convertToDecimal)
{
case 'M':
decimal += 1000;
break;
case 'D':
decimal += 500;
break;
case 'C':
decimal += 100;
break;
case 'L':
decimal += 50;
break;
case 'X':
decimal += 10;
break;
case 'V':
decimal += 5;
break;
case 'I':
decimal += 1;
break;
}
}
if (romanNumeral.contains("IV"))
{
decimal-=2;
}
if (romanNumeral.contains("IX"))
{
decimal-=2;
}
if (romanNumeral.contains("XL"))
{
decimal-=10;
}
if (romanNumeral.contains("XC"))
{
decimal-=10;
}
if (romanNumeral.contains("CD"))
{
decimal-=100;
}
if (romanNumeral.contains("CM"))
{
decimal-=100;
}
return decimal;
}
It will be good if you traverse in reverse.
public class RomanToDecimal {
public static void romanToDecimal(java.lang.String romanNumber) {
int decimal = 0;
int lastNumber = 0;
String romanNumeral = romanNumber.toUpperCase();
/* operation to be performed on upper cases even if user
enters roman values in lower case chars */
for (int x = romanNumeral.length() - 1; x >= 0 ; x--) {
char convertToDecimal = romanNumeral.charAt(x);
switch (convertToDecimal) {
case 'M':
decimal = processDecimal(1000, lastNumber, decimal);
lastNumber = 1000;
break;
case 'D':
decimal = processDecimal(500, lastNumber, decimal);
lastNumber = 500;
break;
case 'C':
decimal = processDecimal(100, lastNumber, decimal);
lastNumber = 100;
break;
case 'L':
decimal = processDecimal(50, lastNumber, decimal);
lastNumber = 50;
break;
case 'X':
decimal = processDecimal(10, lastNumber, decimal);
lastNumber = 10;
break;
case 'V':
decimal = processDecimal(5, lastNumber, decimal);
lastNumber = 5;
break;
case 'I':
decimal = processDecimal(1, lastNumber, decimal);
lastNumber = 1;
break;
}
}
System.out.println(decimal);
}
public static int processDecimal(int decimal, int lastNumber, int lastDecimal) {
if (lastNumber > decimal) {
return lastDecimal - decimal;
} else {
return lastDecimal + decimal;
}
}
public static void main(java.lang.String args[]) {
romanToDecimal("XIV");
}
}
Try this - It is simple and compact and works quite smoothly:
public static int ToArabic(string number) {
if (number == string.Empty) return 0;
if (number.StartsWith("M")) return 1000 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("CM")) return 900 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("D")) return 500 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("CD")) return 400 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("C")) return 100 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("XC")) return 90 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("L")) return 50 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("XL")) return 40 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("X")) return 10 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("IX")) return 9 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("V")) return 5 + ToArabic(number.Remove(0, 1));
if (number.StartsWith("IV")) return 4 + ToArabic(number.Remove(0, 2));
if (number.StartsWith("I")) return 1 + ToArabic(number.Remove(0, 1));
throw new ArgumentOutOfRangeException("something bad happened");
}
assuming the hash looks something like this
Hashtable<Character, Integer> ht = new Hashtable<Character, Integer>();
ht.put('i',1);
ht.put('x',10);
ht.put('c',100);
ht.put('m',1000);
ht.put('v',5);
ht.put('l',50);
ht.put('d',500);
then the logic gets pretty simple going by digit right to left
public static int rtoi(String num)
{
int intNum=0;
int prev = 0;
for(int i = num.length()-1; i>=0 ; i--)
{
int temp = ht.get(num.charAt(i));
if(temp < prev)
intNum-=temp;
else
intNum+=temp;
prev = temp;
}
return intNum;
}
Following your logic of reducing 2 on IX you should reduce 20 on XC 200 on CM and so on.
Less code, more efficient. Not so clearer, sorry!
public int evaluateRomanNumerals(String roman) {
return (int) evaluateNextRomanNumeral(roman, roman.length() - 1, 0);
}
private double evaluateNextRomanNumeral(String roman, int pos, double rightNumeral) {
if (pos < 0) return 0;
char ch = roman.charAt(pos);
double value = Math.floor(Math.pow(10, "IXCM".indexOf(ch))) + 5 * Math.floor(Math.pow(10, "VLD".indexOf(ch)));
return value * Math.signum(value + 0.5 - rightNumeral) + evaluateNextRomanNumeral(roman, pos - 1, value);
}
Imperative + recursive solutions with validation step and online testing
To avoid useless calculations and to make sure the roman numerals format is correct, we need to check the input with a regular expression.
String regex = "^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$";
Explanation of the regular expression in details
Explanation of the regular expression symbols used
Differences between regex functions matches() and find()
Why is MMMCMXCIX (= 3999) the maximum value with standard roman numerals notation
Imperative solution
public static int romanToDecimal(String s) {
if (s == null || s.isEmpty() || !s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"))
return -1;
final Matcher matcher = Pattern.compile("M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I").matcher(s);
final int[] decimalValues = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
final String[] romanNumerals = {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"};
int result = 0;
while (matcher.find())
for (int i = 0; i < romanNumerals.length; i++)
if (romanNumerals[i].equals(matcher.group(0)))
result += decimalValues[i];
return result;
}
try online | try optimized version with comments/explanation online
UPDATE: here are two clever recursive proposals from this thread I solved adding validation step
Recursive solution 1 (original answer)
public class RomanToDecimalConverter {
private static double evaluateNextRomanNumeral(String roman, int pos, double rightNumeral) {
if (pos < 0) return 0;
char ch = roman.charAt(pos);
double value = Math.floor(Math.pow(10, "IXCM".indexOf(ch))) + 5 * Math.floor(Math.pow(10, "VLD".indexOf(ch)));
return value * Math.signum(value + 0.5 - rightNumeral) + evaluateNextRomanNumeral(roman, pos - 1, value);
}
public static int evaluateRomanNumerals(String s) {
if (s == null || s.isEmpty() || !s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"))
return -1;
return (int) evaluateNextRomanNumeral(s, s.length() - 1, 0);
}
}
try online
Recursive solution 2 (original answer)
public class RomanToDecimalConverter {
private static int convertRec(String s) {
if (s.isEmpty()) return 0;
if (s.startsWith("M")) return 1000 + convertRec(s.substring(1));
else if (s.startsWith("CM")) return 900 + convertRec(s.substring(2));
else if (s.startsWith("D")) return 500 + convertRec(s.substring(1));
else if (s.startsWith("CD")) return 400 + convertRec(s.substring(2));
else if (s.startsWith("C")) return 100 + convertRec(s.substring(1));
else if (s.startsWith("XC")) return 90 + convertRec(s.substring(2));
else if (s.startsWith("L")) return 50 + convertRec(s.substring(1));
else if (s.startsWith("XL")) return 40 + convertRec(s.substring(2));
else if (s.startsWith("X")) return 10 + convertRec(s.substring(1));
else if (s.startsWith("IX")) return 9 + convertRec(s.substring(2));
else if (s.startsWith("V")) return 5 + convertRec(s.substring(1));
else if (s.startsWith("IV")) return 4 + convertRec(s.substring(2));
else if (s.startsWith("I")) return 1 + convertRec(s.substring(1));
throw new IllegalArgumentException("Unexpected roman numerals");
}
public static int convert(String s) {
if (s == null || s.isEmpty() || !s.matches("^(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$"))
return -1;
return convertRec(s);
}
}
try online
// Author: Francisco Edmundo
private int translateNumber(String texto) {
int n = 0;
int numeralDaDireita = 0;
for (int i = texto.length() - 1; i >= 0; i--) {
int valor = (int) translateNumber(texto.charAt(i));
n += valor * Math.signum(valor + 0.5 - numeralDaDireita);
numeralDaDireita = valor;
}
return n;
}
private double translateNumber(char caractere) {
return Math.floor(Math.pow(10, "IXCM".indexOf(caractere))) + 5 * Math.floor(Math.pow(10, "VLD".indexOf(caractere)));
}
You can check following code. This code should work on all cases. Also it checks null or empty input and faulty input (Let's say you tried with ABXI)
import java.util.HashMap;
import org.apache.commons.lang3.StringUtils;
public class RomanToDecimal {
private HashMap<Character, Integer> map;
public RomanToDecimal() {
map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000);
}
private int getRomanNumeralValue(char ch) {
if (map.containsKey(ch)) {
return map.get(ch);
}
else {
throw new RuntimeException("Roman numeral string contains invalid characters " + ch);
}
}
public int convertRomanToDecimal(final String pRomanNumeral) {
if (StringUtils.isBlank(pRomanNumeral)) {
throw new RuntimeException("Roman numeral string is either null or empty");
}
else {
int index = pRomanNumeral.length() - 1;
int result = getRomanNumeralValue(pRomanNumeral.charAt(index));
for (int i = index - 1; i >= 0; i--) {
if (getRomanNumeralValue(pRomanNumeral.charAt(i)) >= getRomanNumeralValue(pRomanNumeral.charAt(i + 1))) {
result = result + getRomanNumeralValue(pRomanNumeral.charAt(i));
}
else {
result = result - getRomanNumeralValue(pRomanNumeral.charAt(i));
}
}
return result;
}
}
public static void main(String... args){
System.out.println(new RomanToDecimal().convertRomanToDecimal("XCIX"));
}
}
Full version with error checking and test all valid values in both directions (and some invalid cases).
RomanNumeral.java
import java.util.ArrayList;
import java.util.TreeMap;
/**
* Convert to and from a roman numeral string
*/
public class RomanNumeral {
// used for converting from arabic number
final static TreeMap<Integer, String> mapArabic = new TreeMap<Integer, String>();
// used for converting from roman numeral
final static ArrayList<RomanDigit> mapRoman = new ArrayList<RomanDigit>();
final static int MAX_ARABIC = 3999;
static {
mapArabic.put(1000, "M");
mapArabic.put(900, "CM");
mapArabic.put(500, "D");
mapArabic.put(400, "CD");
mapArabic.put(100, "C");
mapArabic.put(90, "XC");
mapArabic.put(50, "L");
mapArabic.put(40, "XL");
mapArabic.put(10, "X");
mapArabic.put(9, "IX");
mapArabic.put(5, "V");
mapArabic.put(4, "IV");
mapArabic.put(1, "I");
mapRoman.add(new RomanDigit("M", 1000, 3, 1000));
mapRoman.add(new RomanDigit("CM", 900, 1, 90));
mapRoman.add(new RomanDigit("D", 500, 1, 100));
mapRoman.add(new RomanDigit("CD", 400, 1, 90));
mapRoman.add(new RomanDigit("C", 100, 3, 100));
mapRoman.add(new RomanDigit("XC", 90, 1, 9));
mapRoman.add(new RomanDigit("L", 50, 1, 10));
mapRoman.add(new RomanDigit("XL", 40, 1, 9));
mapRoman.add(new RomanDigit("X", 10, 3, 10));
mapRoman.add(new RomanDigit("IX", 9, 1, 0));
mapRoman.add(new RomanDigit("V", 5, 1, 1));
mapRoman.add(new RomanDigit("IV", 4, 1, 0));
mapRoman.add(new RomanDigit("I", 1, 3, 1));
}
static final class RomanDigit {
public final String numeral;
public final int value;
public final int maxConsecutive;
public final int maxNextValue;
public RomanDigit(String numeral, int value, int maxConsecutive, int maxNextValue) {
this.numeral = numeral;
this.value = value;
this.maxConsecutive = maxConsecutive;
this.maxNextValue = maxNextValue;
}
}
/**
* Convert an arabic integer value into a roman numeral string
*
* #param n The arabic integer value
* #return The roman numeral string
*/
public final static String toRoman(int n) {
if (n < 1 || n > MAX_ARABIC) {
throw new NumberFormatException(String.format("Invalid arabic value: %d, must be > 0 and < %d", n, MAX_ARABIC));
}
int leDigit = mapArabic.floorKey(n);
//System.out.println("\t*** floor of " + n + " is " + leDigit);
if (n == leDigit) {
return mapArabic.get(leDigit);
}
return mapArabic.get(leDigit) + toRoman(n - leDigit);
}
/**
* Convert a roman numeral string into an arabic integer value
* #param s The roman numeral string
* #return The arabic integer value
*/
public final static int toInt(String s) throws NumberFormatException {
if (s == null || s.length() == 0) {
throw new NumberFormatException("Invalid roman numeral: a non-empty and non-null value must be given");
}
int i = 0;
int iconsecutive = 0; // number of consecutive same digits
int pos = 0;
int sum = 0;
RomanDigit prevDigit = null;
while (pos < s.length()) {
RomanDigit d = mapRoman.get(i);
if (!s.startsWith(mapRoman.get(i).numeral, pos)) {
i++;
// this is the only place we advance which digit we are checking,
// so if it exhausts the digits, then there is clearly a digit sequencing error or invalid digit
if (i == mapRoman.size()) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: invalid sequence '%s' following digit '%s'",
pos, s.substring(pos), prevDigit != null ? prevDigit.numeral : ""));
}
iconsecutive = 0;
continue;
}
// we now have the match for the next roman numeral digit to check
iconsecutive++;
if (iconsecutive > d.maxConsecutive) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: more than %d consecutive occurences of digit '%s'",
pos, d.maxConsecutive, d.numeral));
}
// invalid to encounter a higher digit sequence than the previous digit expects to have follow it
// (any digit is valid to start a roman numeral - i.e. when prevDigit == null)
if (prevDigit != null && prevDigit.maxNextValue < d.value) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: '%s' cannot follow '%s'",
pos, d.numeral, prevDigit.numeral));
}
// good to sum
sum += d.value;
if (sum > MAX_ARABIC) {
throw new NumberFormatException(
String.format("Invalid roman numeral at pos %d: adding '%s' exceeds the max value of %d",
pos, d.numeral, MAX_ARABIC));
}
pos += d.numeral.length();
prevDigit = d;
}
return sum;
}
}
Main.java
public class Main {
public static void main(String[] args) {
System.out.println("TEST arabic integer => roman numeral string");
for (int i = 0; i<= 4000; i++) {
String s;
try {
s = RomanNumeral.toRoman(i);
}
catch(NumberFormatException ex) {
s = ex.getMessage();
}
System.out.println(i + "\t =\t " + s);
}
System.out.println("TEST roman numeral string => arabic integer");
for (int i = 0; i<= 4000; i++) {
String s;
String msg;
try {
s = RomanNumeral.toRoman(i);
int n = testToInt(s);
assert(i == n); // ensure it is reflexively converting
}
catch (NumberFormatException ex) {
System.out.println(ex.getMessage() + "\t =\t toInt() skipped");
}
}
testToInt("MMMM");
testToInt("XCX");
testToInt("CDC");
testToInt("IVI");
testToInt("XXC");
testToInt("CCD");
testToInt("MDD");
testToInt("DD");
testToInt("CLL");
testToInt("LL");
testToInt("IIX");
testToInt("IVX");
testToInt("IIXX");
testToInt("XCIX");
testToInt("XIWE");
// Check validity via a regexp for laughs
String s = "IX";
System.out.println(s + " validity is " + s.matches("M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})"));
}
private final static int testToInt(String s) {
String msg;
int n=0;
try {
n = RomanNumeral.toInt(s);
msg = Integer.toString(n);
}
catch(NullPointerException | NumberFormatException ex) {
msg = ex.getMessage();
}
System.out.println(s + "\t =\t " + msg);
return n;
}
}
Output
TEST arabic integer => roman numeral string
0 = Invalid arabic value: 0, must be > 0 and < 3999
1 = I
2 = II
3 = III
4 = IV
5 = V
6 = VI
7 = VII
8 = VIII
9 = IX
10 = X
... [snip] ...
3988 = MMMCMLXXXVIII
3989 = MMMCMLXXXIX
3990 = MMMCMXC
3991 = MMMCMXCI
3992 = MMMCMXCII
3993 = MMMCMXCIII
3994 = MMMCMXCIV
3995 = MMMCMXCV
3996 = MMMCMXCVI
3997 = MMMCMXCVII
3998 = MMMCMXCVIII
3999 = MMMCMXCIX
4000 = Invalid arabic value: 4000, must be > 0 and < 3999
TEST roman numeral string => arabic integer
Invalid arabic value: 0, must be > 0 and < 3999 = toInt() skipped
I = 1
II = 2
III = 3
IV = 4
V = 5
VI = 6
VII = 7
VIII = 8
IX = 9
X = 10
... [snip] ...
MMMCMLXXXVIII = 3988
MMMCMLXXXIX = 3989
MMMCMXC = 3990
MMMCMXCI = 3991
MMMCMXCII = 3992
MMMCMXCIII = 3993
MMMCMXCIV = 3994
MMMCMXCV = 3995
MMMCMXCVI = 3996
MMMCMXCVII = 3997
MMMCMXCVIII = 3998
MMMCMXCIX = 3999
Invalid arabic value: 4000, must be > 0 and < 3999 = toInt() skipped
MMMM = Invalid roman numeral at pos 3: more than 3 consecutive occurences of digit 'M'
XCX = Invalid roman numeral at pos 2: 'X' cannot follow 'XC'
CDC = Invalid roman numeral at pos 2: 'C' cannot follow 'CD'
IVI = Invalid roman numeral at pos 2: 'I' cannot follow 'IV'
XXC = Invalid roman numeral at pos 2: invalid sequence 'C' following digit 'X'
CCD = Invalid roman numeral at pos 2: invalid sequence 'D' following digit 'C'
MDD = Invalid roman numeral at pos 2: more than 1 consecutive occurences of digit 'D'
DD = Invalid roman numeral at pos 1: more than 1 consecutive occurences of digit 'D'
CLL = Invalid roman numeral at pos 2: more than 1 consecutive occurences of digit 'L'
LL = Invalid roman numeral at pos 1: more than 1 consecutive occurences of digit 'L'
IIX = Invalid roman numeral at pos 2: invalid sequence 'X' following digit 'I'
IVX = Invalid roman numeral at pos 2: invalid sequence 'X' following digit 'IV'
IIXX = Invalid roman numeral at pos 2: invalid sequence 'XX' following digit 'I'
XCIX = 99
XIWE = Invalid roman numeral at pos 2: invalid sequence 'WE' following digit 'I'
IX validity is true
Lets look at this problem with 3 different scenarios
Scenario 1:
When we see a pattern like the following
'IIIIII' or 'XXXXX' or 'CCCC'
where all characters as the same: We add the value of each characters in the pattern
'IIIIII' gives us '6'
'XXXXX' gives us '50'
'CCCC' gives us '400'
Scenario 2:
When we see any 2 consecutive characters different where first is smaller in value then the 2nd
'IX' or 'XC'
We subtract the value of first from second e.g.
second:'X' gives us '10'
first: 'I' gives us '1'
second - first : 10 - 1 = 9
Scenario 3:
When we see a any 2 consecutive characters different where first is greater in value then the second
'XI' or 'CX'
We add first and second e.g.
second:'I' gives us '10'
first: 'X' gives us '1'
first + second : 10 + 1 = 11
Now we can find the result if we do this recursively.
Here is the java implementation :
//An array to be used for faster comparisons and reading the values
private int[] letters26 = new int[26];
private void init () {
letters26['I' - 'A'] = 1;
letters26['V' - 'A'] = 5;
letters26['X' - 'A'] = 10;
letters26['L' - 'A'] = 50;
letters26['C' - 'A'] = 100;
letters26['D' - 'A'] = 500;
letters26['M' - 'A'] = 1000;
}
public int convertRomanToInteger(String s) {
//Initialize the array
init();
return _convertRomanToInteger(s.toCharArray(), 0);
}
//Recursively calls itself as long as 2 consecutive chars are different
private int _convertRomanToInteger(char[] s, int index) {
int ret = 0;
char pre = s[index];//Char from the given index
ret = _getValue(pre);
//Iterate through the rest of the string
for (int i = index + 1; i < s.length; i++) {
if (compare(s[i], s[i - 1]) == 0) {
//Scenario 1:
//If 2 consecutive chars are similar, just add them
ret += _getValue(s[i]);
} else if (compare(s[i], s[i - 1]) > 0) {
//Scenario 2:
//If current char is greater than the previous e.g IX ('I' s[i - 1] and 'X' s[i - 1])
//We need to calculate starting from 'i' and subtract the calculation ('ret')
//done so far in current call
return _convertRomanToInteger(s, i) - ret;
} else {
//Scenario 3:
//If current char is smaller than the previous e.g XI ('X' s[i - 1] and 'I' s[i - 1])
//We need to calculate starting from 'i' and subtract the result
//from the calculation done so far in current call
return ret + _convertRomanToInteger(s, i);
}
}
return ret;
}
//Helper function for comparison
private int compare(char a, char b) {
return letters26[Character.toUpperCase(a) - 'A']
- letters26[Character.toUpperCase(b) - 'A'];
}
private int _getValue(char c) {
return letters26[Character.toUpperCase(c) - 'A'];
}
Despite the fact that a lot of solutions have been already proposed.
I guess that the following one will be short and clear:
public class RomanToInteger {
public static int romanToInt(String roman) {
final Map<Character, Integer> map = Map.of('I', 1,
'V', 5,
'X', 10,
'L', 50,
'C', 100,
'D', 500,
'M', 1000);
int result = 0;
for (int index = 0; index < roman.length(); index++) {
final int curNumber = map.get(roman.charAt(index));
if (index > 0 && curNumber > map.get(roman.charAt(index - 1))) {
// add current number & remove previous one twice:
// first: we add it before (when it was current number) and removing it for this current number
// second: for correct conversion to roman numbers
result += curNumber - 2 * map.get(roman.charAt(index - 1));
} else {
result += curNumber;
}
}
System.out.printf("%8s -> %4d\n", roman, result);
return result;
}
public static void main(String[] args) {
String[] romans = {"I", "II", "III", "V", "X", "XIV", "XVII", "XX", "XXV",
"XXX", "XXXVIII", "XLIX", "LXIII", "LXXXI", "XCVII", "XCVIII", "XCIX",
"C", "CI", "CCXLVIII", "CCLIII", "DCCXCIX", "MCCCXXV", "MCM", "MM",
"MMCDLVI", "MDCCXV"};
final Instant startTimeIter = Instant.now();
for (String roman : romans) {
romanToInt(roman);
}
final Instant endTimeIter = Instant.now();
System.out.printf("Elapsed time: %d ms\n", Duration.between(startTimeIter, endTimeIter).toMillis());
}
}
Output:
I -> 1
II -> 2
III -> 3
...
MMCDLVI -> 2456
MDCCXV -> 1715
Elapsed time: 101 ms
Logic is quite simple we just go from left to right through roman literal:
if a literal isn't first and the previous one is lower -> we have to add current number to total & extract the previous number twice. First time what we added this at the previous step (when it was current number). Second because it is a rule of roman literals conversion (like IX literal).
otherwise, just add it to the total result.
How about this?
int getdec(const string& input)
{
int sum=0; char prev='%';
for(int i=(input.length()-1); i>=0; i--)
{
if(value(input[i])<sum && (input[i]!=prev))
{ sum -= value(input[i]);
prev = input[i];
}
else
{
sum += value(input[i]);
prev = input[i];
}
}
return sum;
}
Supposing Well-formed Roman numbers:
private static int totalValue(String val)
{
String aux=val.toUpperCase();
int sum=0, max=aux.length(), i=0;
while(i<max)
{
if ((i+1)<max && valueOf(aux.charAt(i+1))>valueOf(aux.charAt(i)))
{
sum+=valueOf(aux.charAt(i+1)) - valueOf(aux.charAt(i));
i+=2;
}
else
{
sum+=valueOf(aux.charAt(i));
i+=1;
}
}
return sum;
}
private static int valueOf(Character c)
{
char aux = Character.toUpperCase(c);
switch(aux)
{
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default:
return 0;
}
}
what about this conversion. no switch, no case at all...
P.S. : I use this script from a bash shell
import sys
def RomanToNum(r):
return {
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}[r]
#
#
#
EOF = "<"
Roman = sys.argv[1].upper().strip()+EOF
num = 0
i = 0
while True:
this = Roman[i]
if this == EOF:
break
n1 = RomanToNum(this)
next = Roman[i+1]
if next == EOF:
n2 = 0
else:
n2 = RomanToNum(next)
if n1 < n2:
n1 = -1 * n1
num = num + n1
i = i + 1
print num
public class RomInt {
String roman;
int val;
void assign(String k)
{
roman=k;
}
private class Literal
{
public char literal;
public int value;
public Literal(char literal, int value)
{
this.literal = literal;
this.value = value;
}
}
private final Literal[] ROMAN_LITERALS = new Literal[]
{
new Literal('I', 1),
new Literal('V', 5),
new Literal('X', 10),
new Literal('L', 50),
new Literal('C', 100),
new Literal('D', 500),
new Literal('M', 1000)
};
public int getVal(String s) {
int holdValue=0;
for (int j = 0; j < ROMAN_LITERALS.length; j++)
{
if (s.charAt(0)==ROMAN_LITERALS[j].literal)
{
holdValue=ROMAN_LITERALS[j].value;
break;
} //if()
}//for()
return holdValue;
} //getVal()
public int count()
{
int count=0;
int countA=0;
int countB=0;
int lastPosition = 0;
for(int i = 0 ; i < roman.length(); i++)
{
String s1 = roman.substring(i,i+1);
int a=getVal(s1);
countA+=a;
}
for(int j=1;j<roman.length();j++)
{
String s2= roman.substring(j,j+1);
String s3= roman.substring(j-1,j);
int b=getVal(s2);
int c=getVal(s3);
if(b>c)
{
countB+=c;
}
}
count=countA-(2*countB);
return count;
}
void disp()
{
int result=count();
System.out.println("Integer equivalent of "+roman+" = " +result);
}
} //RomInt---BLC
I find the following approach a very intuitive one:
public void makeArray(String romanNumeral){
int[] numberArray = new int[romanNumeral.length()];
for(int i=0; i<romanNumeral.length();i++){
char symbol = romanNumeral.charAt(i);
switch(symbol){
case 'I':
numberArray[i] = 1;
break;
case 'V':
numberArray[i] = 5;
break;
case 'X':
numberArray[i] = 10;
break;
case 'L':
numberArray[i] = 50;
break;
case 'C':
numberArray[i] = 100;
break;
case 'D':
numberArray[i] = 500;
break;
case 'M':
numberArray[i] = 1000;
break;
}
}
calculate(numberArray);
}
public static void calculate(int[] numberArray){
int theNumber = 0;
for(int n=0;n<numberArray.length;n++){
if(n !=numberArray.length-1 && numberArray[n] < numberArray[n+1]){
numberArray[n+1] = numberArray[n+1] - numberArray[n];
numberArray[n] = 0;
}
}
for(int num:numberArray){
theNumber += num;
}
System.out.println("Converted number: " + theNumber);
}
//Bet no one has a smaller and easier logic than this........Open CHALLENGE!!!!!!!
import java.io.*;
class Convertion_practical_q2
{
void Decimal()throws IOException //Smaller code for convertion from roman to decimal
{
DataInputStream in=new DataInputStream(System.in);
System.out.println("Enter the number");
String num=in.readLine();
char pos[]={'0','I','V','X','L','C','D','M'};
int l1=7; //l1 is size of pos array
String v[]={"","1","5","10","50","100","500","1000"};
int l=num.length();
int p=0,p1=0,sum=0;
for(int i=l-1;i>=0;i--)
{
char ch=num.charAt(i);
for(int j=1;j<=l1;j++)
{
if(ch==pos[j])
p=j;
}
if(p>=p1)
sum+=Integer.parseInt(v[p]);
else
sum-=Integer.parseInt(v[p]);
//System.out.println("sum ="+sum+"\np="+p+"\np1="+p1);
p1=p;
}
System.out.println(sum);
}
}
Just got it working in Java, nice job guys.
public int getDecimal (String roman) {
int decimal = 0;
int romanNumber = 0;
int prev = 0;
for (int i = roman.length()-1; i >= 0; i--){
romanNumber = hashRomans.get(roman.charAt(i));
if(romanNumber < decimal && romanNumber != prev ){
decimal -= romanNumber;
prev = romanNumber;
} else {
decimal += romanNumber;
prev = romanNumber;
}
}
return decimal;
}
This should work:
import java.io.*;
import java.util.Scanner;
enum RomanToNumber {
i(1), v(5), x(10), l(50), c(100); int value;
RomanToNumber (int p){value = p;}
int getValue(){return value;}
}
public class RomanToInteger {
public static void main(String[] args){
RomanToNumber n;
System.out.println( "Type a valid roman number in lower case" );
String Str = new String(new Scanner(System.in).nextLine());
int n1 = 0, theNo = 0, len = Str.length();
int[] str2No = new int [len];
for(int i=0; i < len; i++){
n = RomanToNumber.valueOf(Str.substring(n1, ++n1));
str2No[i] = n.getValue();
}
for(int j = 0; j < (len-1); j++){
if( str2No[j] >= str2No[j+1] ){ theNo += str2No[j]; }
else{ theNo -= str2No[j]; }
}
System.out.println( theNo += str2No[len-1] );
}
}
Since most of the answers here are in Java, I'm posting the answer in C++ (since I am bored right now and nothing more productive to do :) But please, no downvotes except if code is wrong.
Known-issues = will not handle overflow
Code:
#include <unordered_map>
int convert_roman_2_int(string& str)
{
int ans = 0;
if( str.length() == 0 )
{
return ans;
}
std::unordered_map<char, int> table;
table['I'] = 1;
table['V'] = 5;
table['X'] = 10;
table['L'] = 50;
table['C'] = 100;
table['D'] = 500;
table['M'] = 1000;
ans = table[ str[ str.length() - 1 ] ];
for( int i = str.length() - 2; i >= 0; i--)
{
if(table[ str[i] ] < table[ str[i+1] ] )
{
ans -= table[ str[i] ];
}
else
{
ans += table[ str[i] ];
}
}
return ans;
}
// test code
void run_test_cases_convert_roman_to_int()
{
string roman = "VIII";
int r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "XX";
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "CDX"; //410
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "MCMXC"; //1990
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "MMVIII"; //2008
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
roman = "MDCLXVI"; //1666
r = convert_roman_2_int(roman);
cout << roman << " in int is " << r << endl << flush;
}
this is very basic implementation of what you asked and working for all the test cases. If you find anything wrong in it than do mention it,i will try to make it correct also the code is in C++ .
int RomanToInt(string s){
int len = s.length();
map<char,int>mp;
int decimal = 0;
mp['I'] = 1;mp['V'] = 5;mp['X'] = 10;
mp['L'] = 50;mp['C'] = 100;mp['D'] = 500;mp['M'] = 1000;
for(int i = 0; i < len ;i++){
char cur = s[i],fast = s[i+1];
int cur_val = mp[cur],fast_val = mp[fast];
if(cur_val < fast_val){
decimal = decimal - cur_val;
}else{
decimal += cur_val;
}
}
return decimal;
}
public static int convertFromRoman(String romanNumeral)
{
Character[] rnChars = { 'M', 'D', 'C', 'L', 'X', 'V', 'I' };
int[] rnVals = { 1000, 500, 100, 50, 10, 5, 1 };
HashMap<Character, Integer> valueLookup = new HashMap<Character, Integer>();
for (int i = 0; i < rnChars.length;i++)
valueLookup.put(rnChars[i], rnVals[i]);
int retVal = 0;
for (int i = 0; i < romanNumeral.length();i++)
{
int addVal = valueLookup.get(romanNumeral.charAt(i));
retVal += i < romanNumeral.length()-1 &&
addVal < valueLookup.get(romanNumeral.charAt(i+1))?
-addVal:addVal;
}
return retVal;
}
This is a modest variation of the recursive algorithm suggested by Sahtiel:
public static int toArabic(String number) throws Exception {
String[] letras = {"M","CM","D","CD","C","XC","L","XL","X", "IX","V","IV","I"};
int[] valores = {1000,900,500,400,100,90,50,40,10,9,5,4,1};
// here we can do even more business validations like avoiding sequences like XXXXM
if (number==null || number.isEmpty()) {
return 0;
}
for(int i=0; i<letras.length; i++) {
if (number.startsWith(letras[i])) {
return valores[i] + toArabic(number.substring(letras[i].length()));
}
}
throw new Exception("something bad happened");
}
It uses less than 10 effective lines of code.
This scala solution might be useful:
class RomanNumberConverter {
private val toArabic = Map('I' -> 1, 'V' -> 5, 'X' -> 10, 'L' -> 50, 'C' -> 100, 'D' -> 500, 'M' -> 1000)
// assume that correct roman number is provided
def convert(romanNumber: String): Int = {
def convert(rn: StringBuilder, lastDecimal: Int, acc: Int): Int = {
if (rn.isEmpty) acc
else {
val thisDecimal = toArabic(rn.head)
if (thisDecimal > lastDecimal) convert(rn.tail, thisDecimal, acc + thisDecimal - lastDecimal - lastDecimal)
else convert(rn.tail, thisDecimal, acc + thisDecimal)
}
}
val sb = new StringBuilder(romanNumber)
convert(sb.tail, toArabic(sb.head), toArabic(sb.head))
}
}
Solution using tail recursion:
import java.util.LinkedHashMap;
public class RomanNumber {
private final static LinkedHashMap<String, Integer> roman2number = new LinkedHashMap<>(); // preserve key order
static {
roman2number.put("M", 1000);
roman2number.put("CM", 900);
roman2number.put("D", 500);
roman2number.put("CD", 400);
roman2number.put("C", 100);
roman2number.put("XC", 90);
roman2number.put("L", 50);
roman2number.put("XL", 40);
roman2number.put("X", 10);
roman2number.put("IX", 9);
roman2number.put("V", 5);
roman2number.put("IV", 4);
roman2number.put("I", 1);
}
public final static Integer toDecimal(String roman) {
for (String key : roman2number.keySet()) {
if (roman.startsWith(key)) {
if (roman.equals(key)) {
return roman2number.get(key);
}
return roman2number.get(key) + toDecimal(roman.substring(key.length()));
}
}
return 0;
}
}
Testing:
import junitparams.JUnitParamsRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import junitparams.Parameters;
import static org.junit.Assert.assertTrue;
#RunWith(JUnitParamsRunner.class)
public class RomanNumberTest {
#Test
#Parameters({ "1|I", "2|II", "3|III", "4|IV", "5|V", "6|VI", "7|VII", "8|VIII", "9|IX", "10|X",
"11|XI", "12|XII", "13|XIII", "14|XIV", "15|XV", "16|XVI", "17|XVII", "18|XVIII", "19|XIX",
"20|XX", "50|L", "53|LIII", "57|LVII", "40|XL", "49|XLIX", "59|LIX", "79|LXXIX", "100|C", "90|XC", "99|XCIX",
"200|CC", "500|D", "499|CDXCIX", "999|CMXCIX", "2999|MMCMXCIX", "3999|MMMCMXCIX"
})
public void forRomanReturnsNumber(int number, String roman) {
assertTrue(roman + "->" + number, RomanNumber.toDecimal(roman) == (number));
}
}
With only two if-statements:
Check the match of first character on priority and if not matching, match the single first character.
public class Solution {
private static TreeMap<String, Integer> numeralsAndVal;
static{
numeralsAndVal = new TreeMap<>();
numeralsAndVal.put("M", 1000);
numeralsAndVal.put("CM", 900);
numeralsAndVal.put("D", 500);
numeralsAndVal.put("CD", 400);
numeralsAndVal.put("C", 100);
numeralsAndVal.put("XC", 90);
numeralsAndVal.put("L", 50);
numeralsAndVal.put("XL", 40);
numeralsAndVal.put("X", 10);
numeralsAndVal.put("IX", 9);
numeralsAndVal.put("V", 5);
numeralsAndVal.put("IV", 4);
numeralsAndVal.put("I", 1);
}
public int romanToInt(String A) {
if (A.length() == 0) return 0;
if (A.length() >= 2 && numeralsAndVal.containsKey(A.substring(0, 2))){
return numeralsAndVal.get(A.substring(0, 2)) + romanToInt(A.substring(2));
}
return numeralsAndVal.get(A.substring(0, 1)) + romanToInt(A.substring(1));
}
}
For converting TO arabic, it works as follows:
grab the rightmost unchecked character and convert it to its numeric value using the map. If this value is less than the "top" value we've seen so far, then it needs to be subtracting (e.g. IV needs to be 4, not 6), otherwise we add the value to the total and make it the new top value we've seen so far. So, for instance, XIX works like follows: 'X' is checked and comes out to be value of 10. That is higher than the "top" value seen so far, so it becomes "top" and we add 10 to the value. Then go to the next character and "I" is < 10, so subtract
public class RomanNumeral {
private final Map<Integer, String> arabicToRoman = new LinkedHashMap<Integer, String>();
private final Map<String, Integer> romanToArabic = new LinkedHashMap<String, Integer>();
public RomanNumeral() {
arabicToRoman.put(10, "X");
arabicToRoman.put(9, "IX");
arabicToRoman.put(5, "V");
arabicToRoman.put(4, "IV");
arabicToRoman.put(1, "I");
romanToArabic.put("X", 10);
romanToArabic.put("V", 5);
romanToArabic.put("I", 1);
}
public String convertToRomanNumeral(int number) {
String result = "";
for (Integer i : arabicToRoman.keySet()) {
while (number >= i) {
result += arabicToRoman.get(i);
number -= i;
}
}
return result;
}
public String convertToArabicNumber(String romanNumeral) {
int result = 0;
int top = 0;
for (int i = romanNumeral.length() - 1; i >= 0; i--) {
char current = romanNumeral.charAt(i);
int value = romanToArabic.get("" + current);
if (value < top) {
result -= value;
} else {
result += value;
top = value;
}
}
return "" + result;
}
}
An easy solution for input in the range from 1 to 3999.
private static final Map<String, Integer> MAPPING = new HashMap<>() {{
put("I", 1);
put("V", 5);
put("X", 10);
put("L", 50);
put("C", 100);
put("D", 500);
put("M", 1000);
put("IV", 4);
put("IX", 9);
put("XL", 40);
put("XC", 90);
put("CD", 400);
put("CM", 900);
}};
public static int romanToInt(String s) {
int sum = 0;
for (int i = 0; i < s.length(); i++) {
if (i < s.length() - 1 && MAPPING.containsKey(String.valueOf(s.charAt(i)) + s.charAt(i + 1))) {
sum += MAPPING.get(String.valueOf(s.charAt(i)) + s.charAt(i + 1));
i++;
} else {
sum += MAPPING.get(String.valueOf(s.charAt(i)));
}
}
return sum;
}
The problem here is that numbers like 9 are presented in the following way
IX = 9
if we take the literal conversion by getting the sum for each number than our program would give us the following sum
I = 1
X = 10
X + I = 11
which is not correct.
We can overcome this if we simply manipulate the Roman String.
In example:
IX = 9 would be VIIII which is 5 + 1 + 1 + 1 + 1
Therefore if we replace all the special cases in the given String, we can calculate the sum for each character.
Hence;
class Solution {
public int romanToInt(String s) {
// Key Value pairs
Map<Character, Integer> map = new HashMap<>();
map.put('I', 1);
map.put('V', 5);
map.put('X', 10);
map.put('L', 50);
map.put('C', 100);
map.put('D', 500);
map.put('M', 1000);
// convert these to single characters
s = s.replace("IV","IIII");
s = s.replace("IX","VIIII");
s = s.replace("XL","XXXX");
s = s.replace("XC","LXXXX");
s = s.replace("CD","CCCC");
s = s.replace("CM","DCCCC");
// total sum to be returned
int sum = 0;
// convert s to c character array
char[] c = s.toCharArray();
// iterate through c array and sum the numbers
for(Character ch : c){
sum = sum + map.get(ch);
}
return sum;
}
}
Here is my one...
import java.util.Scanner;
class Solution {
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
String num;
// System.out.println("enter the number you want to convert from roman to integer.");
num = "D";
System.out.println("length of string is " + num.length());
System.out.println("the integer number is :" + romanToInt(num) );
}
public static int romanToInt(String s) {
char I,V, X, L,C, D, M;
char c1,c3, c2 = s.charAt(0);
// System.out.println("the c2 is : " + (int)c2);
int num = 0, num1 = 0;
int j =0, k = 0, temp = 0;
if (c2 == 'I') {
k = (int)c2 - 72;
System.out.println("k is I" + k);
} else if(c2 == 'V') {
k = (int)c2 - 81;
System.out.println("K is V" + k);
// return 86 - 81;
} else if (c2 == 'X') {
k = (int)c2 - 78;
System.out.println("K is X" + k);
} else if (c2 == 'L') {
k = (int)c2 - 26;
System.out.println("K is L" + k);
} else if (c2 == 'C') {
k = (int)c2 + 33;
System.out.println("K is C" + k);
} else if (c2 == 'D') {
k = (int)c2 + 432;
System.out.println("K is D" + k);
} else if ( c2 == 'M') {
k = (int)c2 + 923;
System.out.println("K is M" + k);
}
if (s.length() == 1){
num = k;
} else {
for(int i = 1; i<= s.length()-1 ; i++) {
System.out.println("i is : " + i);
c1 = s.charAt(i);
if (i == s.length() - 1) {
temp = 0;
} else {
c3 = s.charAt(i+1);
if (c3 == 'I') {
temp = (int)c3 - 72;
System.out.println("temp is I " + temp);
} else if(c3 == 'V') {
temp = (int)c3 - 81;
System.out.println("temp is I " + temp);
// return 86 - 81;
} else if (c3 == 'X') {
temp = (int)c3 - 78;
System.out.println("temp is I " + temp);
} else if (c3 == 'L') {
temp = (int)c3 - 26;
System.out.println("temp is I " + temp);
} else if (c3 == 'C') {
temp = (int)c3 + 33;
System.out.println("temp is I " + temp);
} else if (c3 == 'D') {
temp = (int)c3 + 432;
System.out.println("temp is I " + temp);
} else if ( c3 == 'M') {
temp = (int)c3 + 923;
System.out.println("temp is I " + temp);
}
}
if (c1 == 'I') {
j = (int)c1 - 72;
System.out.println("j is I " + j);
} else if(c1 == 'V') {
j = (int)c1 - 81;
System.out.println("j is V " + j);
// return 86 - 81;
} else if (c1 == 'X') {
j = (int)c1 - 78;
System.out.println("j is X " + j);
} else if (c1 == 'L') {
j = (int)c1 - 26;
System.out.println("j is L " + j);
} else if (c1 == 'C') {
j = (int)c1 + 33;
System.out.println("j is C " + j);
} else if (c1 == 'D') {
j = (int)c1 + 432;
System.out.println("j is D " + j);
} else if ( c1 == 'M') {
j = (int)c1 + 923;
System.out.println("j is M " + j);
}
if ( k < j && j>temp ) {
k = j - k ;
num = num + k;
} else if (j==k || j<k || j<temp){
num = num + k ;
// k = j;
}
if (j>k ) {
k = temp;
i += 1;
if (i == s.length()-1) {
num = num + k;
}
} else {
k = j;
if (i == s.length()-1) {
num = num + k;
}
}
}
}
return num;
}
}