检查罗马数字的有效性(困难)

时间:2016-06-11 18:58:19

标签: java roman-numerals

我想编写一个函数来检查罗马演示文稿中的给定字符串是否正确。它们是很多非允许组合的情况:(我假设给定的字符串代表1到3999之间的数字)

  • 我们不能连续三次拥有相同的角色:例如:IIII是假的。
  • 不允许某些组合:DD为假('D'+'D'= 500 + 500 = 1000即M
  • 我们不能减去一个字符并在之后添加相同的字符:例如,如果IX为9,则IXI不正确,它不等于9 + 1
  • 最重要的数字必须位于开头而不是中间或结尾。例如:XM(1010)为假,而MX是正确的。
  • 等...

所以,我的想法是,不是检查不允许的组合,而是写下所有可能允许的组合,每当我们遇到不在其中的组合时,我们将返回false。这是我的想法。不方便的是,我的最终功能很长,而且不太容易理解。

例如,我首先编写了一个检查数千个函数的函数(如果它们当然存在),然后函数返回索引,我将用它来将当前字符串子串到移动到下一个部分(这将是数百个)在那种情况下):

private static int isThousandsValid(String str){
    int len = str.length();

    char a1 = str.charAt(0);
    char a2 = (len >= 2)? str.charAt(1) : ' ';
    char a3 = (len >= 3)? str.charAt(2) : ' ';

    if (a1 == 'M' && a2 == 'M' && a3 == 'M') //if we met that combinatin
        return 3; //we have to move after 3 digits to meet the beginning
                      //of the hundred digits
    else if (a1 == 'M' && a2 == 'M')   //same raisoning for other combinations
        return 2;

    else if (a1 == 'M') 
        return 1;

    else if (a1 == 'D' || a1 == 'C' || a1 == 'L'  || a1 == 'X' || a1 == 'V' || a1 == 'I'  )
        return 0;

    else return -1;

}

然后,我为数百,数十和单位写了同样的东西。数百的例子:

private static int isHundredsValid(String str){
    if (str.isEmpty()) return 0;
    int len = str.length();

    char a1 = str.charAt(0);
    char a2 = (len >= 2)? str.charAt(1) : ' ';
    char a3 = (len >= 3)? str.charAt(2) : ' ';
    char a4 = (len >= 4)? str.charAt(3) : ' ';

    if (a1 == 'C' && a2 == 'M') 
        return 2;

    else if (a1 == 'D' && a2 == 'C' && a3 == 'C' && a4 == 'C')
        return 4;

    else if (a1 == 'D' && a2 == 'C' && a3 == 'C') 
        return 3;

    else if (a1 == 'D' && a2 == 'C') 
        return 2;

    else if (a1 == 'D') 
        return 1;

    else if (a1 == 'C' && a2 == 'D') 
        return 2;

    else if (a1 == 'C' && a2 == 'C' && a3 == 'C') 
        return 3;

    else if (a1 == 'C' && a2 == 'C') 
        return 2;

    else if (a1 == 'C') 
        return 1;

    else if (a1 == 'L'  || a1 == 'X' || a1 == 'V' || a1 == 'I'  )
        return 0;

    else return -1;     
}

然后,在我的最后一个函数中,我写了这个:

    public static boolean isValidRoman(String str){
    str = str.trim(); //remove spaces
    if (str.isEmpty()) return false;

    int index1 = isThousandsValid(str);     
    String str1 = mySubstring(str, index1);

    int index2 = isHundredsValid(str1);     
    String str2 = mySubstring(str1, index2);

    int index3  = isTensValid(str2);        
    String str3 = mySubstring(str2, index3);

    int index4 = isUnitsValid(str3);        
    String str4 = mySubstring(str3, index4);

    if (str1.isEmpty() || str2.isEmpty() || str3.isEmpty())
        return true;

    if (index1 == -1 || index2 ==-1 || index3 == -1 || index4 == -1)
        return false;

    return str4.isEmpty(); //if we still have ANOTHER character after it terminates
}

最后,“mySubstring”只是一个我用来重构和清除代码的简单函数:

 private static String mySubstring(String str, int index){
    if (index == -1 ) return str;
    else 
        return str.substring(index);
}

我有两个主要问题: 这个功能对你来说是否正确?我在很多例子中测试过,但我不确定(我无法测试所有3999种可能的组合......)

是否有可能改善它?只是为了让它更清晰或更具可读性? 有没有更简单的方法来检查罗马数字的有效性而不是写出所有这些情况?

1 个答案:

答案 0 :(得分:1)

我会选择简短而疯狂的解决方案并使用正则表达式匹配字符串:

public boolean isRoman(String s)
{
    return !s.isEmpty() 
           && s.matches("M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})");
}