我必须确保输入的字母仅由 M,D,C,L,X,V和I 组成,我已经完成了。
我的问题是它们是罗马数字,我还必须确保如果较低值字符位于较高值字符之前,则它遵循罗马数字的规则:
CM
或DM
可以,但LM
不是。XL
可以,但VL
不是。7个字母代表如下数值:
╔════════╦═══════╗
║ Symbol ║ Value ║
╠════════╬═══════╣
║ I ║ 1 ║
║ V ║ 5 ║
║ X ║ 10 ║
║ L ║ 50 ║
║ C ║ 100 ║
║ D ║ 500 ║
║ M ║ 1,000 ║
╚════════╩═══════╝
这就是我到目前为止:
void romanType::storeRoman()
{
locale loc;
bool valid = true;
do
{
valid = true;
cout << "please enter a roman numeral using no spaces:" << endl;
getline(cin, rNums);
for (i = 0; i < rNums.length(); ++i)
{
rNums[i] = toupper(rNums[i], loc);
if (rNums[i] == 'M' || rNums[i] == 'D' || rNums[i] == 'C' ||
rNums[i] == 'L' || rNums[i] == 'X' || rNums[i] == 'V' ||
rNums[i] == 'I')
{
continue;
}
valid = false;
}
cout << "input error please try again\n" << endl;
} while (!valid);
/* ... */
}
只要所有字符都是罗马数字,它就可以工作,但我不能为我的生活弄清楚如何实现我提到的其他2个约束。我已经完成了大部分程序的其余部分,但我花了大约6到7个小时试图让这一部分工作。请帮忙。
答案 0 :(得分:2)
遵循您的规则,有效罗马数 MCMLIV
(1954)将被视为无效,例如C
前面不能M
,L
前面不能M
I
。因此,您的规则是错误的或不完整的。
但是,如果您改为使用这些规则(取自Wikipedia),那么它将起作用:
V
可以放在X
和IV
之前,分别制作4个单位(IX
)和9个单位(X
)L
可以放在C
和XL
之前分别制作40(XC
)和90(C
)D
可以放在M
和CD
之前,根据相同的模式制作400(CM
)和900(IVXLCDM
)。现在,你的功能基本上需要执行两件事:
// The string representing a roman number.
std::string s = "MCMLIV";
// Check that the predicate is true for all elements in range.
const std::string allowedChars = "IVXLCDM";
bool valid = std::all_of(std::begin(s), std::end(s), [&allowedChars] (char c) {
return allowedChars.find(::toupper(c)) != std::string::npos;
});
if (!valid) {
std::cerr << "Input error" << std::endl;
return EXIT_FAILURE;
}
)您的功能是第一个,但它可以简化。我们要做的就是在字符串中找到任何无效字符。我们可以使用std::all_of
检查所有字符是否有效。
// Check if we can find any pair that does not comply to the rules.
auto it = std::adjacent_find(std::begin(s), std::end(s), [] (char lhs, char rhs)
{
lhs = std::toupper(lhs); // Transform to upper case.
rhs = std::toupper(rhs); // Transform to upper case.
return ((lhs == 'I' && rhs != 'V') && (lhs == 'I' && rhs != 'X')) ||
((lhs == 'X' && rhs != 'L') && (lhs == 'X' && rhs != 'C')) ||
((lhs == 'C' && rhs != 'D') && (lhs == 'C' && rhs != 'M'));
});
if (it != std::end(s)) {
std::cerr << "Input error" << std::endl;
return EXIT_FAILURE;
}
然后我们需要根据规则成对检查字符。我们可以使用std::adjacent_find
:
I
就是这样!请参阅此live example。
修改强>
您还必须检查IIIIIII
连续发生的次数是否超过3次,因此不要接受{{1}}字符串。这应该相当容易。
答案 1 :(得分:0)
一种方法(来自C心态)可能是列出合法字符,以及与之对应的合法“抵消”(定义如下):
#define LEGALCHAR 7 // number of possible legal letters in roman numerals
char legalValues[LEGALCHAR] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
int allowedDelta[LEGALCHAR] = {0, 1, 1, 1, 2, 2, 2};
并创建一个int IndexedNumeral
数组,其中包含来自匹配字符的legalValues
的索引,例如MMX将是{6,6,2}。然后循环遍历IndexedNumeral
的第2个到最后一个条目,以比较i-1
,i
个字符与i
个字符允许的合法偏移之间的差异。
diff = indexedNumeral[i] - indexedNumeral[i-1];
if (diff > allowedDelta[indexedNumeral[i]]) {
printf("illegal sequence: %i %i or %c %c\n", indexedNumeral[i-1], indexedNumeral[i], argv[1][i-1], argv[1][i]);
return 1;
}
当然还有其他方法。