Check if an input is a valid roman numeral

后端 未结 5 604
醉话见心
醉话见心 2021-01-14 10:46

I got a program that converts Roman numerals to integers and vice versa. My problem is that I don´t really know how to create a function that checks if the user input is a v

5条回答
  •  伪装坚强ぢ
    2021-01-14 11:13

    Writing a converter from ints to Romans is a standard interview question. I once wrote the following bi-directional implementation (toString-- decimal to Roman; parse -- Roman to decimal). The implementaion saticifies a number of additional criteria on the representation of Roman numbers, which are not obligatory, but generally followed:

    '''
    Created on Feb 7, 2013
    
    @author: olegs
    '''
    
    ROMAN_CONSTANTS = (
                ( "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" ),
                ( "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" ),
                ( "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" ),
                ( "", "M", "MM", "MMM", "",   "",  "-",  "",    "",     ""   ),
            )
    
    ROMAN_SYMBOL_MAP = dict(I=1, V=5, X=10, L=50, C=100, D=500, M=1000)
    
    CUTOFF = 4000
    BIG_DEC = 2900
    BIG_ROMAN = "MMCM"
    ROMAN_NOUGHT = "nulla"
    
    def digits(num):
        if num < 0:
            raise Exception('range error: negative numbers not supported')
        if num % 1 != 0.0:
            raise Exception('floating point numbers not supported')
        res = []
        while num > 0:
            res.append(num % 10)
            num //= 10
        return res
    
    def toString(num, emptyZero=False):
        if num < CUTOFF:
            digitlist = digits(num)
            if digitlist:
                res = reversed([ ROMAN_CONSTANTS[order][digit] for order, digit in enumerate(digitlist) ])
                return "".join(res)
            else:
                return "" if emptyZero else ROMAN_NOUGHT 
        else:
            if num % 1 != 0.0:
                raise Exception('floating point numbers not supported')
            # For numbers over or equal the CUTOFF, the remainder of division by 2900
            # is represented as above, prepended with the multiples of MMCM (2900 in Roman),
            # which guarantees no more than 3 repetitive Ms.
            return BIG_ROMAN * (num // BIG_DEC) + toString(num % BIG_DEC, emptyZero=True)
    
    def parse(numeral):
        numeral = numeral.upper()
        result = 0
        if numeral == ROMAN_NOUGHT.upper():
            return result
        lastVal = 0
        lastCount = 0
        subtraction = False
        for symbol in numeral[::-1]:
            value = ROMAN_SYMBOL_MAP.get(symbol)
            if not value:
                raise Exception('incorrect symbol')
            if lastVal == 0:
                lastCount = 1
                lastVal = value
            elif lastVal == value:
                lastCount += 1
                # exceptions
            else:
                result += (-1 if subtraction else 1) * lastVal * lastCount
                subtraction = lastVal > value
                lastCount = 1
                lastVal = value
        return result + (-1 if subtraction else 1) * lastVal * lastCount
    

提交回复
热议问题