将加密的罗马数字转换为阿拉伯小数


16

编写一种算法,将字母序列解释为罗马数字。(请参阅下面的罗马数字规则)

每个不同的字母都有一个匹配的阿拉伯十进制值,没有最大值。但是您事先没有密钥,因此{A=10, I=1, X=5, ... Z=1000000}取决于您的解释。

挑战

  1. 通过读取输入 STDIN或等效和写入输出经由STDOUT或等效
  2. 有效输入是大小写字母的组合,即匹配 \[a-zA-Z]+\
  3. 输入应经过验证,以查看字母序列是否可以解释为有效的罗马数字
  4. 如果输入通过验证,则有效输出应该是最低的阿拉伯十进制解释,并且所使用的键即Aa解释为4 {a=5, A=1} not 6 {A=5, a=1} 9 {a=10, a=1}

罗马数字规则

  1. 只能重复表示十次幂的字母,最多重复三次,总共重复四次,例如 II III XXXIX

  2. 如果将一个或多个字母放置在另一个价值更高的字母之后,请添加该金额

    AAaa   => 22 {A=10, a=1}          (20 + 2 = 22)  
    bbAAaa => 222 {b=100, A=10, a=1}  (200 + 20 + 2 = 222)   
    
  3. 如果将一个字母放在另一个较大值的字母之前,请减去该数量

    Aa    => 4 {a=5, A=1}                 (5 – 1 = 4)  
    AaA   => 19 {A=10, a=1}               (10 + 10 – 1 = 19)  
    BbBaA => 194 {B=100, b=10, A=5, a=1}  (100 + 100 - 10 + 5 - 1 = 194)  
    

    有几个规则适用于从罗马数字中减去金额:

    • 仅减去十的幂,即1, 10, 100... 5, 50, 500...
    • 因此,没有双重减法18被写成XVIII 不是 IIXX (10 + 10 - 1 - 1)
    • 不要从大于十倍的数字中减去一个数字。
      您可以减去15 10,但50, 100, 500...

Input:

Aa  
BAa  
CCCXLVII   
MMMCDVII  
ABADDF  
XVVX  
FAASGSH  
DXCCDA  
AaBbcDEf   

Output:

4 {a=5, A=1}  
14 {B=10, a=5, A=1}  
347 {C=100, L=50, X=10, V=5, I=1}  
347 {M=100, D=50, C=10, V=5, I=1}  
1921 {A=1000, B=100, D=10, F=1}  
'XVVX' failed Roman numeral test  
7191 {F=5000, A=1000, S=100, G=10, H=1}  
'DXCCDA' failed Roman numeral test
4444 {a=5000, A=1000, b=500, B=100, D=50, c=10, f=5, E=1}  

3
@IamOgbz,这已经变成了一个很好的问题,但是一路上在评论中吸引了很多问题。现在您已经有足够的声誉,我建议您使用sandbox。我发现这对于在发布之前就提出问题非常有用。
trichoplax

CCCLXVII不会解释为CCCXLVII,给出347吗?
斯凯勒

@Skyler,您绝对正确,将立即更新!谢谢。
iamogbz 2015年

对于单个字母可以具有的值,我没有看到任何限制(实际上您提到的是20,这不是标准罗马数字的值)。您的意思是说任何正整数都可以用罗马数字表示吗?在这种情况下,Aa其值为1(A = 1,a = 2)。
msh210

@ msh210,因为字母只能解释为罗马数字,因此,单个字母值只能是10的幂或5的10的幂。 19不是有效的减法)。希望能为您清除它。
iamogbz 2015年

Answers:


1

Python 2中,415 444 440 419 416字节

毕竟,罗马数字并不多。该脚本创建所有它们并检查输入的所有排列,然后返回最小的匹配项。

a=raw_input()
g=range
b=list(set(a))+[' ']*9
from itertools import*
c=[]
s={}
u=1000
for i in g(10*u):
 t,f=(10*u,9*u,5*u,4*u,u,900,500,400,100,90,50,40,10,9,5,4,1),i;r=""
 for j in g(17):k=i/t[j];r+=('W MW Q MQ M CM D CD C XC L XL X IX V IV I').split()[j]*k;i-=t[j]*k
 s[r]=f
for i in permutations(b[:9]):
 r=''
 for j in a:r+='IVXLCMQWE'[i.index(j)]
 if r in s:c+=[s[r]]
print c and min(c)or'%s failed Roman numeral test'%a

这对现在的挑战是一个很好的答案。但是在早期被删除的评论对话中,暗示了这个(不是真实的)系统在M = 1000之后继续,其符号为5k,10k等。请看顶部的第一个示例:{A = 10,I = 1,X = 5,... Z = 1000000}由您的解释决定
edc65

..,最后一个例子是a = 5000 ...
edc65

我更新了它以处理所有给定的测试用例。我怀疑这种方法是否可以扩展到10,000个以上,因为这需要花费O(n!)时间来表示罗马数字。
斯凯勒

@Skyler测试用例并不详尽。该程序应处理可以按照罗马数字规则解释的有效输入的所有可能排列,在模棱两可的情况下,优先选择较小的数字。另外,您的代码无法处理最后的测试用例链接
iamogbz 2015年

是不是import itertools as i然后i.permutations较短?
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.