Why is it so difficult to accessing characters in a string?
If strings would be made of fixed sized characters stored in arrays, finding the n-th character would be straightforward and efficient using an integer subscript.
But popular string encodings such as UTF-8 and UTF-16 use a variable number of bytes/words to store characters. So a direct access to the n-th character requires counting characters one by one from the start or other costly strategies, to deal correctly with strings such as "Abçdefg"
(9 characters, but 11 UTF16 words and 16 UTF8 bytes)
Quick fix
Since the subscript requires an index and you work with integers, you can get rid of the error, by replace the faulty s[i]
with:
s[s.index(s.startIndex, offsetBy: i))]
This is not very concise. But it has the advantage of drawing your attention at the complexity of computing and indexes from an integer character count. (And if you'd profile your code working with very long strings, you'll quickly find out it's a bottleneck).
How about making better use of indexes?
On the other side, many algorithms (including yours) access characters one after another. So there is no need to recount characters from the start over and over again, if you work with relative positions. For this reason, Swift designers chose to subscript strings with indexes.
A better approach would therefore be to use Swift's native way: work with indexes instead of integers and iterate over the string:
func lengthOfLongestSubstring(_ s: String) -> Int {
var maximum = 0
var start = s.startIndex // starting at start of the string
var end = s.startIndex
var set = Set<Character>()
while end != s.endIndex{ // is end of string reached ?
if set.contains(s[end]){
set.remove(s[start])
start = s.index(after: start) // going to the next character
}
else{
set.insert(s[end])
end = s.index(after:end) // going to the next character
maximum = max(maximum, s.distance(from: start, to:end)) // count characters between indexes
}
}
return maximum
}
Here a quick introduction on useful indexing functions.