When in Rome, Count as Romans do?
Python 2, 177 168 162 bytes
import re,itertools as q
f=lambda n:sum(None!=re.match("^M*(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$",(''.join(m)))for m in q.product('MDCLXVI',repeat=n))
Try it online!
I'm pretty new, help me golf this! This checks for actual roman numerals, the regex needs to be adjusted to account for the odd cases such as IVI
-9 bytes thanks to @Dead Possum!
-6 bytes thanks to @ovs
JavaScript (ES7), 133 bytes
Edit: Fixed to match the results returned by Jonathan Allan's code, which was given as a reference implementation by the OP.
n=>[...Array(m=k=7**n)].reduce(s=>s+/^1*5?4{0,3}3?2{0,3}6?0{0,3}$/.test((--k+m).toString(7).replace(/0[62]|2[34]|4[51]/g,s=>s[1])),0)
Try it online!
How?
1) We generate all numbers of \$N\$ digits in base 7 with an extra leading \$1\$:
[...Array(m = k = 7 ** n)].reduce(s => … (--k + m).toString(7) …, 0)
From now on, each digit will be interpreted as a Roman numeral symbol:
$$\begin{array}{}0\longleftrightarrow \text{I}, & 1\longleftrightarrow \text{M}, & 2\longleftrightarrow \text{X}, & 3\longleftrightarrow \text{L},\\ 4\longleftrightarrow \text{C}, & 5\longleftrightarrow \text{D}, & 6\longleftrightarrow \text{V} \end{array}$$
2) We replace all valid subtractive pairs of the form AB
with B
:
.replace(/0[62]|2[34]|4[51]/g, s => s[1])) // in the code
.replace(/I[VX]|X[LC]|C[DM]/g, s => s[1])) // with Roman symbols
Examples:
XLIXIV
becomesLXV
XIIV
becomesXIV
, leaving aI
that will make the next test failIC
remains unchanged, which also leaves an invalidI
in place
3) We check that the remaining symbols are in the correct order and do not appear more times than they're allowed to:
/^1*5?4{0,3}3?2{0,3}6?0{0,3}$/.test(…) // in the code
/^M*D?C{0,3}L?X{0,3}V?I{0,3}$/.test(…) // with Roman symbols
Retina, 111 bytes
~(`.+
*$(CM)CDXCXCXCXLIXIXIXIVII
.(.)
.+¶$$&$¶$$&$1$¶$$&$&¶L`.{0,$+}\b¶D`¶
¶$
¶.+¶$$&$¶$$&I¶L`[A-Z]{$+}\b¶D`¶.+
Try it online! This is a complete rewrite as I misunderstood rule 1. to mean that you could only use one each of subtractive I
, X
and C
. Explanation: The first part of the script expands the input into a string of CM
pairs followed by the other possible subtractive pairs. Each pair is optional, and the first character of each pair is also optional within the pair. The third stage then expands the list of pairs into a list of Retina commands that take the input and create three copies with the option of the second or both characters from the pair, then trims and deduplicates the results. The final stage then appends code to perform the final tasks: first to expand the input to possibly add a final I
, then to filter out results of the wrong length, then to deduplicate the results, and finally to count the results. The resulting Retina script is then evaluated.
Note: In theory 15 bytes could be saved from the end of the 4th line but this makes the script too slow to demonstrate on TIO even for n=1
.