在检查和删除符号时,字符串索引有问题。我该如何改进?
func romanToInt(_ s: String) -> Int {
let romanDigits = ["I" : 1,
"V" : 5,
"X" : 10,
"L" : 50,
"C" : 100,
"D" : 500,
"M" : 1000]
let romanSums = ["IV" : 4,
"IX" : 9,
"XL" : 40,
"XC" : 90,
"CD" : 400,
"CM" : 900]
var sum = 0
var str = s
var charIndex = str.startIndex
for index in str.indices {
if index != str.index(before: str.endIndex) {
charIndex = str.index(after: index)
} else {
charIndex = str.index(before: str.endIndex)
}
let chars = String(str[index]) + String(str[charIndex])
if romanSums[chars] != nil {
print(chars)
str.remove(at: charIndex)
sum += romanSums[chars]!
print(sum)
} else {
let char = String(str[index])
print(char)
sum += romanDigits[char]!
print(sum)
}
print(str)
}
return sum
}
let check = romanToInt("MCMXCIV")
控制台日志:
M 1000 MCMXCIV CM 1900 MCXCIV XC 1990 MCXIV IV 1994 MCXI Fatal error: Can't advance past endIndex
您正在修改您正在迭代的字符串,因此您的索引变得无效。相反,您可以添加skipChar
布尔值,该布尔值说您已经处理了下一个字符,然后通过执行continue
跳过该字符:
func romanToInt(_ s: String) -> Int {
let romanDigits = ["I" : 1,
"V" : 5,
"X" : 10,
"L" : 50,
"C" : 100,
"D" : 500,
"M" : 1000]
let romanSums = ["IV" : 4,
"IX" : 9,
"XL" : 40,
"XC" : 90,
"CD" : 400,
"CM" : 900]
var sum = 0
var str = s
var charIndex = str.startIndex
var skipChar = false
for index in str.indices {
if skipChar {
skipChar = false
continue
}
if index != str.index(before: str.endIndex) {
charIndex = str.index(after: index)
} else {
charIndex = str.index(before: str.endIndex)
}
let chars = String(str[index]) + String(str[charIndex])
if romanSums[chars] != nil {
print(chars)
skipChar = true
sum += romanSums[chars]!
print(sum)
} else {
let char = String(str[index])
print(char)
sum += romanDigits[char]!
print(sum)
}
print(str)
}
return sum
}
let check = romanToInt("MCMXCIV")
print(check)
1994
for index in str.indices {
...
str.remove(at: charIndex)
在您迭代时修改字符串是无效的。str.indices
在这里获取一次,并且一旦修改了基础字符串。
我敢肯定,这将有很多实现,因为这是一个吸引实施的小型有趣的问题。那为什么不呢?这只是向我尖叫递归。
let romanDigits: [Substring: Int] = ["I" : 1,
"V" : 5,
"X" : 10,
"L" : 50,
"C" : 100,
"D" : 500,
"M" : 1000]
let romanSums: [Substring: Int] = ["IV" : 4,
"IX" : 9,
"XL" : 40,
"XC" : 90,
"CD" : 400,
"CM" : 900]
func romanToInt<S: StringProtocol>(_ s: S) -> Int
where S.SubSequence == Substring {
if s.isEmpty { return 0 }
if let value = romanSums[s.prefix(2)] {
return value + romanToInt(s.dropFirst(2))
} else if let value = romanDigits[s.prefix(1)] {
return value + romanToInt(s.dropFirst(1))
} else {
fatalError("Invalid string")
}
}
let check = romanToInt("MCMXCIV")
当然,这实际上并没有检查有效的序列,所以这是一种垃圾。" iiiixxiii"有点胡言乱语,但起作用。但这与原始方法保持一致。
使用 reduce
在此处流动:
func romanToInt(_ s: String) -> Int {
if s.isEmpty {return 0}
let romanDigits = ["I" : 1,
"V" : 5,
"X" : 10,
"L" : 50,
"C" : 100,
"D" : 500,
"M" : 1000]
let romanSums = ["IV" : 4,
"IX" : 9,
"XL" : 40,
"XC" : 90,
"CD" : 400,
"CM" : 900]
return s.dropFirst().reduce((s.first!, romanDigits["(s.first!)"]!)){
return ( $1, //current char
$0.1 + //previous sum
(romanSums["($0.0)($1)"] //add double value
?? ((romanDigits["($1)"]!). + romanDigits["($0.0)"]!)) //or single value and add duplicated
- romanDigits["($0.0)"]!) // minus duplicated
}.1
}
print(romanToInt("MCMXCIV")). //1994
您正在在循环内突变str
,其最终索引正在变化,在这种情况下,它的原始值低于其原始值。您可以通过检查每次迭代的endIndex
来修复代码:
var index = str.startIndex
while index < str.endIndex {
...
//Before the closing curly brace of the while loop
index = str.index(after: index)
}
我试图复制一个用户报告的崩溃,其中一个消息Can't advance past endIndex
,但我无法做到。您的代码帮助我弄清楚此错误在以后版本的Swift中发生了变化。
您的相同代码将用Swift 4.x运行时库报告cannot increment beyond endIndex
,而String index is out of bounds
则以5.x。我不知道的更改的确切版本号。但我怀疑它是4.0.0和5.0.0 .-