How to solve LeetCode problem #12 Integer to Roman

Javascript – LeetCode problem #12 Integer to Roman

In this problem, we need to create a method that translates an Integer (1<=n<=3999) to a Roman numeral. Read the full problem on LeetCode.

Pattern – Deconstruct the Integer

The key to solving this problem is understanding the construction pattern of Roman numerals:

  1. They are read as a sum of their parts and are generally written largest to smallest (thousands, hundreds, tens, singles)
    • => we need to deconstruct the Integer to thousand, hundreds, tens, singles
      • 3210 => 3 thousands, 2 hundreds, 1 ten, 0 singles => MMMCCX
  2. however, we need to keep the exception in mind where a number in a threshold position is represented by subtraction:
    • I = 1; II =2; III =3; IV=4; V=5
    • V=5; VI = 6; VII=7; VIII =8; IX =9; X =10
    • X=10; XX=20; XXX =30; XL =40; L = 50
    • 2439 => 2 thousands, 4 hundreds, 3 tens, 9 singles => MMCDXXXIX

In summary, we need to look at each digit of the Integer and its “weigth” (thousands, hundres, etc.), then determine the correct Roman numeral to represent it.

Solution – Integer to Roman

Mapping values and preparing the main method

For my solution, I first setup a map that maps each “range” (hundreds, tens, singles) to the Roman numerals can be used to construct a number in that range.

const romanMap = new Map([
    //hundreds
    ["C", {
        top : "M", //1000
        mid : "D", //500
        self : "C" // 100
    }],
    //tens
    ["X", {
        top : "C", // 100
        mid : "L", // 50
        self : "X" // 10
    }],
    //singles
    ["I", {
        top : "X", // 10
        mid : "V", // 5
        self : "I" // 1
    }],
]);

Example: 
Looking at section singles, every number between 1-9 can constructed with the letters listed under "I" (I,V,X)
1=I, 2=II, 3=III, 4=IV, 5=V, 6=VI, 7=VII, 8=VIII, 9=IX

Next, I am working on the function itself. I’m initializing my output in var romanStr as an empty string. Because we need to separately evaluate each digit of the integer, I am converting the Integer (num) to a string and then splitting it into an array (arrNum). I’m also grabbing the length of the resulting array (arrLength).

As a first condition, if the array is empty, we can return an empty string.

var intToRoman = function(num) {
    let romanStr = "";
    let arrNum = num.toString().split("");
    let arrLength = arrNum.length;
    if(arrLength===0){
        return romanStr;
    }
}
Example:
num=432
num.toString()= "432"
num.toString().split("") = ["4","3","2"]

Let’s work on the additional conditions for checking the array. We already know if the array is empty, we have nothing to evaluate and return an empty string. Additionally we know:

  • if the array length is 4, we have a number with 4 “sections” (thousands, hundreds,tens,singles)
  • if the array length is 3, we have a number with 3 “sections” (hundreds, tens, singles)
  • if the array length is 2, we have a number with 2 “sections” (tens, singles)
  • if the array length is 1, we have a number with 1 “section” (singles)

As seen below, processing thousands is pretty straightforward. The problem constraints state the n <= 3999, which means we can just add an M to our romanStr for each digit at the 0th index of arrNum.

if(arrLength===0){
    return romanStr;
}
else if(arrLength == 4){
    // process thousands
    for(let i=0; i <arrNum[0]; i++){
        romanStr += "M";
    }
    // process hundreds, tens, singles
}
else if(arrLength == 3){
    // process hundreds, tens, singles
}
else if(arrLength == 2){
    // process tens, singles
}
else {
    // process singles
} 
Example:
num= 3201
numArr = ["3","2","0","1"]
arrLength = 4
arrNum[0] = 3
=> for loop adds 3 "M"s to our string
romanStr = "MMM"

Creating a helper method

To process hundreds, tens and singles, we need some more complex logic to allow for specially constructed numerals. For this, I created a helper function getRomanRep(n,type). n is the digit we are evaluating and type is the digits “weight” that we have mapped at the very start. For example, n=3 type=X means we are evaluating 3 in the space of tens, aka 30. n=7 type=C means we are evaluating 7 in the space of hundreds, aka 700.

In this helper function, we need one more if…else statement that evaluates n and then uses its type to get the correct letters to construct the Roman numeral representation.

  • n>0 && n<=3 | this is the simplest construction, add the corresponding letter from the map n-times
    • n=2, type = X ; we are getting section X from the map, then selecting the .self value (X) and adding it n-times => “XX” or 20
  • n==4 | we need to construct this Roman numeral as a combination of letters
    • n=4, type = C; we are getting section C from the map and adding a .self value (C) before the .mid value (D) => “CD” or 400
  • n==5 | we need to construct this Roman numeral by taking the mid value of its mapped values
    • n=5, type = I; we are getting section I from the map and adding .mid value (V) => “V” or 5
  • n>5 && n<9 | we need to construct this Roman numeral by taking the .mid value and adding (n-5) .self values
    • n=7, type=X; we are getting section X from the map, first add .mid value (L) and then 2 .self values (X) => “LXX” or 70
  • n==9 | we need to construct this Roman numeral as a combination of letters
    • n=9, type = C; we are getting section C from the map and adding a .self value (C) before the .top value (M) => “CM” or 900
function getRomanRep(n,type){
    let strOut = "";
    if(n>0 && n<=3){
        for(let i=0; i<n; i++){
            strOut += romanMap.get(type).self;
        }
    }
    else if(n==4){
        strOut += romanMap.get(type).self;
        strOut += romanMap.get(type).mid;
    }
    else if(n==5){
        strOut = romanMap.get(type).mid;
    }
    else if(n>5 && n<9){
        strOut += romanMap.get(type).mid;
        for(let i=0; i<n-5; i++){
            strOut += romanMap.get(type).self;
        }
    }
    else if(n==9){
        strOut += romanMap.get(type).self;
        strOut += romanMap.get(type).top;
    }
    return strOut;
}

Full Solution – Integer to Roman

Now it’s time to put it all together! In our main method intToRoman(), I am now calling the helper method for each digit that needs to be evaluated and passing through the correct map key (“C” for hundreds, etc.). Each evaluation adds to romanStr which I am returning at the end. See the full code below:

const romanMap = new Map([
    //hundreds
    ["C", {
        top : "M", //1000
        mid : "D", //500
        self : "C" // 100
    }],
    //tens
    ["X", {
        top : "C", // 100
        mid : "L", // 50
        self : "X" // 10
    }],
    //singles
    ["I", {
        top : "X", // 10
        mid : "V", // 5
        self : "I" // 1
    }],
]);

var intToRoman = function(num) {
    let arrNum = num.toString().split("");
    let arrLength = arrNum.length;
    let romanStr = "";
    if(arrLength === 0){
        return romanStr;
    }
    else if(arrLength == 4){
        for(let i=0; i <arrNum[0]; i++){
            romanStr += "M";
        }
        romanStr += getRomanRep(arrNum[1],'C');
        romanStr += getRomanRep(arrNum[2],'X');
        romanStr += getRomanRep(arrNum[3],'I');
    }
    else if(arrLength === 3){
        romanStr += getRomanRep(arrNum[0],'C');
        romanStr += getRomanRep(arrNum[1],'X');
        romanStr += getRomanRep(arrNum[2],'I');
    }
    else if(arrLength === 2){
        romanStr += getRomanRep(arrNum[0],'X');
        romanStr += getRomanRep(arrNum[1],'I');
    }
    else{
        romanStr += getRomanRep(arrNum[0],'I');
    }
    return romanStr;
    
};

function getRomanRep(n,type){
    let strOut = "";
    if(n>0 && n<=3){
        for(let i=0; i<n; i++){
            strOut += romanMap.get(type).self;
        }
    }
    else if(n==4){
        strOut += romanMap.get(type).self;
        strOut += romanMap.get(type).mid;
    }
    else if(n==5){
        strOut = romanMap.get(type).mid;
    }
    else if(n>5 && n<9){
        strOut += romanMap.get(type).mid;
        for(let i=0; i<n-5; i++){
            strOut += romanMap.get(type).self;
        }
    }
    else if(n==9){
        strOut += romanMap.get(type).self;
        strOut += romanMap.get(type).top;
    }
    return strOut;
}

This is how I solved LeetCode problem #12 Integer to Roman in Javascript. See other LeetCode solutions