118 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			118 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| 
								 | 
							
								---
							 | 
						||
| 
								 | 
							
								id: 5900f3c51000cf542c50fed7
							 | 
						||
| 
								 | 
							
								title: '問題 89: ローマ数字'
							 | 
						||
| 
								 | 
							
								challengeType: 5
							 | 
						||
| 
								 | 
							
								forumTopicId: 302204
							 | 
						||
| 
								 | 
							
								dashedName: problem-89-roman-numerals
							 | 
						||
| 
								 | 
							
								---
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# --description--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								ローマ数字で書かれた数字が有効とみなされるには、従うべき基本ルールがあります。 ルール上、複数の方法で表現できる数がいくつかありますが、特定の数字を書く*最良*の方法が必ず存在します。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								- 数字は大きさの降順に並べなければならない。
							 | 
						||
| 
								 | 
							
								- M, C, X は、それよりも小さい単位と等しくなったりそれに超えられたりしてはならない。
							 | 
						||
| 
								 | 
							
								- D, L, V はそれぞれ一度のみ使用できる。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								減算の組み合わせを用いる場合には、上の 3 つのルールに加えて次の 4 つのルールに従う必要があります。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								- 減算ペア部分の先頭には、I, X, C をそれぞれ 1 つのみ使用できる
							 | 
						||
| 
								 | 
							
								- I は V と X の前にしか置けない。
							 | 
						||
| 
								 | 
							
								- X は L と C の前にしか置けない。
							 | 
						||
| 
								 | 
							
								- C は D と M の前にしか置けない。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								例えば、16 という数の書き方は少なくとも 6 通りあるように思えるでしょう。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								<div style="margin-left: 4em; font-family: 'courier new', monospace;">
							 | 
						||
| 
								 | 
							
								  IIIIIIIIIIIIIIII<br>
							 | 
						||
| 
								 | 
							
								  VIIIIIIIIIII<br>
							 | 
						||
| 
								 | 
							
								  VVIIIIII<br>
							 | 
						||
| 
								 | 
							
								  XIIIIII<br>
							 | 
						||
| 
								 | 
							
								  VVVI<br>
							 | 
						||
| 
								 | 
							
								  XVI<br><br>
							 | 
						||
| 
								 | 
							
								</div>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								しかしルールによれば、XIIIIII と XVI のみが有効です。最後の例は、使う数字が最も少ないので、最も効率が良いといえます。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								配列 `roman` には、有効ではあるが必ずしも最小形式ではないローマ数字が含まれています。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								それらの各数字を最小形式で書くことで文字をいくつ減らせるか答えなさい。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								**注:** 配列内のすべてのローマ数字について、同じ単位が 4 つより多く連続することはないと仮定できます。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# --hints--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								`romanNumerals(testNumerals1)` は数値を返す必要があります。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								assert(typeof romanNumerals(_testNumerals1) === 'number');
							 | 
						||
| 
								 | 
							
								```
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								`romanNumerals(testNumerals1)` は `19` を返す必要があります。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								assert.strictEqual(romanNumerals(_testNumerals1), 19);
							 | 
						||
| 
								 | 
							
								```
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								`romanNumerals(testNumerals2)` は `743` を返す必要があります。
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								assert.strictEqual(romanNumerals(_testNumerals2), 743);
							 | 
						||
| 
								 | 
							
								```
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# --seed--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								## --after-user-code--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								const _testNumerals1 = [
							 | 
						||
| 
								 | 
							
								  'XIIIIII', 'XVI', 'MMMCCLXVIIII', 'XXXXVI', 'MMMMXX', 'CCLI', 'CCCCXX', 'MMMMDCXXXXI', 'DCCCCIIII', 'MXVIIII'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								const _testNumerals2 = [
							 | 
						||
| 
								 | 
							
								  'MMMMDCLXXII','MMDCCCLXXXIII','MMMDLXVIIII','MMMMDXCV','DCCCLXXII','MMCCCVI','MMMCDLXXXVII','MMMMCCXXI','MMMCCXX','MMMMDCCCLXXIII','MMMCCXXXVII','MMCCCLXXXXIX','MDCCCXXIIII','MMCXCVI','CCXCVIII','MMMCCCXXXII','MDCCXXX','MMMDCCCL','MMMMCCLXXXVI','MMDCCCXCVI','MMMDCII','MMMCCXII','MMMMDCCCCI','MMDCCCXCII','MDCXX','CMLXXXVII','MMMXXI','MMMMCCCXIV','MLXXII','MCCLXXVIIII','MMMMCCXXXXI','MMDCCCLXXII','MMMMXXXI','MMMDCCLXXX','MMDCCCLXXIX','MMMMLXXXV','MCXXI','MDCCCXXXVII','MMCCCLXVII','MCDXXXV','CCXXXIII','CMXX','MMMCLXIV','MCCCLXXXVI','DCCCXCVIII','MMMDCCCCXXXIV','CDXVIIII','MMCCXXXV','MDCCCXXXII','MMMMD','MMDCCLXIX','MMMMCCCLXXXXVI','MMDCCXLII','MMMDCCCVIIII','DCCLXXXIIII','MDCCCCXXXII','MMCXXVII','DCCCXXX','CCLXIX','MMMXI','MMMMCMLXXXXVIII','MMMMDLXXXVII','MMMMDCCCLX','MMCCLIV','CMIX','MMDCCCLXXXIIII','CLXXXII','MMCCCCXXXXV','MMMMDLXXXVIIII','MMMDCCCXXI','MMDCCCCLXXVI','MCCCCLXX','MMCDLVIIII','MMMDCCCLIX','MMMMCCCCXIX','MMMDCCCLXXV','XXXI','CDLXXXIII','MMMCXV','MMDCCLXIII','MMDXXX','MMMMCCCLVII','MMMDCI','MMMMCDLXXXIIII','MMMMCCCXVI','CCCLXXXVIII','MMMMCML','MMMMXXIV','MMMCCCCXXX','DCCX','MMMCCLX','MMDXXXIII','CCCLXIII','MMDCCXIII','MMMCCCXLIV','CLXXXXI','CXVI','MMMMCXXXIII','CLXX','DCCCXVIII','MLXVII','DLXXXX','MMDXXI','MMMMDLXXXXVIII','MXXII','LXI','DCCCCXLIII','MMMMDV','MMMMXXXIV','MDCCCLVIII','MMMCCLXXII','MMMMDCCXXXVI','MMMMLXXXIX','MDCCCLXXXI','MMMMDCCCXV','MMMMCCCCXI','MMMMCCCLIII','MDCCCLXXI','MMCCCCXI','MLXV','MMCDLXII','MMMMDXXXXII','MMMMDCCCXL','MMMMCMLVI','CCLXXXIV','MMMDCCLXXXVI','MMCLII','MMMCCCCXV','MMLXXXIII','MMMV','MMMV','DCCLXII','MMDCCCCXVI','MMDCXLVIII','CCLIIII','CCCXXV','MMDCCLXXXVIIII','MMMMDCLXXVIII','MMMMDCCCXCI','MMMMCCCXX','MMCCXLV','MMMDCCCLXIX','MMCCLXIIII','MMMDCCCXLIX','MMMMCCCLXIX','CMLXXXXI','MCMLXXXIX','MMCDLXI','MMDCLXXVIII','MMMMDCCLXI','MCDXXV','DL','CCCLXXII','MXVIIII','MCCCCLXVIII','CIII','MMMDCCLXXIIII','MMMDVIII','MMMMCCCLXXXXVII','MMDXXVII','MMDCCLXXXXV','MMMMCXLVI','MMMDCCLXXXII','MMMDXXXVI','MCXXII','CLI','DCLXXXIX','MMMCLI','MDCLXIII','MMMMDCCXCVII','MMCCCLXXXV','MMMDCXXVIII','MMMCDLX','MMMCMLII','MMMIV','MMMMDCCCLVIII','MMMDLXXXVIII','MCXXIV','MMMMLXXVI','CLXXIX','MMMCCCCXXVIIII','DCCLXXXV','MMMDCCCVI','LI','CLXXXVI','MMMMCCCLXXVI','MCCCLXVI','CCXXXIX','MMDXXXXI','MMDCCCXLI','DCCCLXXXVIII','MMMMDCCCIV','MDCCCCXV','MMCMVI','MMMMCMLXXXXV','MMDCCLVI','MMMMCCXLVIII','DCCCCIIII','MMCCCCIII','MMMDCCLXXXVIIII','MDCCCLXXXXV','DVII','MMMV','DCXXV','MMDCCCXCV','DCVIII','MMCDLXVI','MCXXVIII','MDCCXCVIII','MMDCLX','MMMDCCLXIV','MMCDLXXVII','MMDLXXXIIII','MMMMCCCXXII','MMMDCCCXLIIII','DCCCCLXVII','MMMCLXXXXIII','MCCXV','MMMMDCXI','MMMMDCLXXXXV','MMMCCCLII','MMCMIX','MMDCCXXV','MMDLXXXVI','MMMMDCXXVIIII','DCCCCXXXVIIII','MMCCXXXIIII','MMDCCLXXVIII','MDCCLXVIIII','MMCCLXXXV','MMMMDCCCLXXXVIII','MMCMXCI','MDXLII','MMMMDCCXIV','MMMMLI','DXXXXIII','MMDCCXI','MMMMCCLXXXIII','MMMDCCCLXXIII','MDCLVII','MMCD','MCCCXXVII','MMMMDCCIIII','MMMDCCXLVI','MMMCLXXXVII','MMMCCVIIII','MCCCCLXXIX','DL','DCCCLXXVI','MMDXCI','MMMMDCCCCXXXVI','MMCII','MMMDCCCXXXXV','MMMCDXLV','MMDCXXXXIV','MMD','MDCCCLXXXX','MMDCXLIII','MMCCXXXII','MMDCXXXXVIIII','DCCCLXXI','MDXCVIIII','MMMMCCLXXVIII','MDCLVIIII','MMMCCCLXXXIX','MDCLXXXV','MDLVIII','MMMMCCVII','MMMMDCXIV','MMMCCCLXIIII','MMIIII','MMMMCCCLXXIII','CCIII','MMMCCLV','MMMDXIII','MMMCCCXC','MMMDCCCXXI','MMMMCCCCXXXII','CCCLVI','MMMCCCLXXXVI','MXVIIII','MMMCCCCXIIII','CLXVII','MMMCCLXX','CCCCLXIV','MMXXXXII','MMMMCCLXXXX','MXL','CCXVI','CCCCLVIIII','MMCCCII','MCCCLVIII','MMMMCCCX','MCDLXXXXIV','MDCCCXIII','MMDCCCXL','MMMMCCCXXIII','DXXXIV','CVI','MMMMDCLXXX','DCCCVII','MMCMLXIIII','MMMDCCCXXXIII','DCCC','MDIII','MMCCCLXVI','MMMCCCCLXXI','MMDCCCCXVIII','CCXXXVII','CCCXXV','MDCCCXII','MMMCMV','MMMMCMXV','MMMMDCXCI','DXXI','MMCCXLVIIII','MMMMCMLII','MDLXXX','MMDCLXVI','CXXI','MMMDCCCLIIII','MMMCXXI','MCCIII','MMDCXXXXI','CCXCII','MMMMDXXXV','MMMCCCLXV','MMMMDLXV','MMMCCCCXXXII','MMMCCCVIII','DCCCCLXXXXII','MMCLXIV','MMMMCXI','MLXXXXVII','MMMCDXXXVIII','MDXXII','MLV','MMMMDLXVI','MMMCXII','XXXIII','MMMMDCCCXXVI','MMMLXVIIII','MMMLX','MMMCDLXVII','MDCCCLVII','MMCXXXVII','M
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								```
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								## --seed-contents--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								function romanNumerals(roman) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return true;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Only change code above this line
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const testNumerals1 = [
							 | 
						||
| 
								 | 
							
								  'XIIIIII', 'XVI', 'MMMCCLXVIIII', 'XXXXVI', 'MMMMXX', 'CCLI', 'CCCCXX', 'MMMMDCXXXXI', 'DCCCCIIII', 'MXVIIII'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								romanNumerals(testNumerals1);
							 | 
						||
| 
								 | 
							
								```
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								# --solutions--
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								```js
							 | 
						||
| 
								 | 
							
								function romanNumerals(roman) {
							 | 
						||
| 
								 | 
							
								  const numerals = [...roman];
							 | 
						||
| 
								 | 
							
								  const replaces = [
							 | 
						||
| 
								 | 
							
								    ['VIIII', 'IX'],
							 | 
						||
| 
								 | 
							
								    ['IIII', 'IV'],
							 | 
						||
| 
								 | 
							
								    ['LXXXX', 'XC'],
							 | 
						||
| 
								 | 
							
								    ['XXXX', 'XL'],
							 | 
						||
| 
								 | 
							
								    ['DCCCC', 'CM'],
							 | 
						||
| 
								 | 
							
								    ['CCCC', 'CD']
							 | 
						||
| 
								 | 
							
								  ];
							 | 
						||
| 
								 | 
							
								  let savedChars = 0;
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < numerals.length; i++) {
							 | 
						||
| 
								 | 
							
								    const charsBefore = numerals[i].length;
							 | 
						||
| 
								 | 
							
								    for (let j = 0; j < replaces.length; j++) {
							 | 
						||
| 
								 | 
							
								      numerals[i] = numerals[i].replace(...replaces[j]);
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    const charsAfter = numerals[i].length;
							 | 
						||
| 
								 | 
							
								    savedChars += charsBefore - charsAfter;
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return savedChars;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								```
							 |