检查罗马数字的有效性(困难)
check for the validity of a roman number (difficult)
我想编写一个函数来检查给定的罗马表示字符串是否正确。他们是很多不允许组合的情况:(我假设给定的字符串将代表 1 到 3999 之间的数字)
- 同一字符不能连续超过三次 : ex : IIII 为假。
- 某些组合是不允许的:DD 为假 ('D' + 'D' = 500 + 500 = 1000 即 M
- 我们不能减去一个字符并在后面添加相同的字符:例如,如果 IX 是 9,则 IXI 是不正确的事件,它不等于 9 + 1
- 最重要的数字必须在开头,而不是在中间或末尾。例如:XM(对于 1010)是错误的,而 MX 是正确的。
- 等...
所以,我的想法是,与其检查不允许的组合,不如写下所有可能的允许组合,每次遇到不在其中的组合时,我们都会 return 错误。那是我的主意。不方便的是我的最终函数很长而且不太容易理解。
例如,我首先编写了一个函数来检查数千(当然如果它们存在),然后该函数 returns 我将使用的索引来对当前字符串进行子串以移动到下一部分(在这种情况下将有数百个):
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 种可能的组合...)
是否可以改进?只是为了让它更干净或更具可读性?
有没有比写所有这些情况更简单的方法来检查罗马数字的有效性??
我会选择简短而疯狂的解决方案,并使用正则表达式匹配字符串:
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})");
}
我想编写一个函数来检查给定的罗马表示字符串是否正确。他们是很多不允许组合的情况:(我假设给定的字符串将代表 1 到 3999 之间的数字)
- 同一字符不能连续超过三次 : ex : IIII 为假。
- 某些组合是不允许的:DD 为假 ('D' + 'D' = 500 + 500 = 1000 即 M
- 我们不能减去一个字符并在后面添加相同的字符:例如,如果 IX 是 9,则 IXI 是不正确的事件,它不等于 9 + 1
- 最重要的数字必须在开头,而不是在中间或末尾。例如:XM(对于 1010)是错误的,而 MX 是正确的。
- 等...
所以,我的想法是,与其检查不允许的组合,不如写下所有可能的允许组合,每次遇到不在其中的组合时,我们都会 return 错误。那是我的主意。不方便的是我的最终函数很长而且不太容易理解。
例如,我首先编写了一个函数来检查数千(当然如果它们存在),然后该函数 returns 我将使用的索引来对当前字符串进行子串以移动到下一部分(在这种情况下将有数百个):
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 种可能的组合...)
是否可以改进?只是为了让它更干净或更具可读性? 有没有比写所有这些情况更简单的方法来检查罗马数字的有效性??
我会选择简短而疯狂的解决方案,并使用正则表达式匹配字符串:
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})");
}