Opening the reading…
Opening the reading…
HASHING › IMPLEMENTARY PROBLEMS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →Roman numerals are built from large values toward small values, but six values are special: 4, 9, 40, 90, 400, and 900. Each special value is written as one symbol before another, such as IV or CM. If each ordinary and subtractive form is treated as one value-symbol pair, the conversion rule becomes uniform instead of requiring a separate branch for every decimal place.
Scan those pairs from largest to smallest. Whenever the current value fits inside num, append its symbol and subtract its value. A larger pair must be considered first, because using smaller symbols too early can produce a different spelling: 900 must become CM before 500 and 400 are considered. After the scan, num is zero and the appended symbols are already in Roman numeral order.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(1) | The scan examines 13 fixed value-symbol pairs, and each pair can be appended only a bounded number of times for numbers from 1 through 3999. The amount of work does not grow beyond that fixed range. |
| Space | O(1) extra | The pair table and loop variables use constant working memory. The returned Roman numeral is required output and is excluded; even in the largest allowed input, its length is bounded by the fixed input range. |
#include <string>
#include <utility>
#include <vector>
using namespace std;
class Solution {
public:
string intToRoman(int num) {
vector<pair<int, string>> values = {
{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"}
};
string result;
for (const auto& [value, symbol] : values) {
while (num >= value) {
result += symbol;
num -= value;
}
}
return result;
}
};The while loop is important even though many entries are used once. It allows values such as 1000, 100, 10, and 1 to repeat, so 3749 can consume three M symbols and then three C symbols. The subtractive entries do not need separate code: 900 is consumed as CM, 90 as XC, and 4 as IV because those complete forms appear before their smaller components.
A competent alternative is to split num into its thousands, hundreds, tens, and ones digits, then select one prewritten string for each digit. This is merely a different arrangement, not an asymptotic optimisation: both methods use constant time and constant extra space under the stated range. The lookup version makes the four decimal places explicit, while the greedy version generalises more naturally when the numeral rules are represented as value-symbol pairs.
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
string intToRoman(int num) {
const vector<string> thousands = {"", "M", "MM", "MMM"};
const vector<string> hundreds = {
"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"
};
const vector<string> tens = {
"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"
};
const vector<string> ones = {
"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"
};
string result;
result += thousands[num / 1000];
result += hundreds[(num / 100) % 10];
result += tens[(num / 10) % 10];
result += ones[num % 10];
return result;
}
};