The Swift standard library does not have a built-in method to access the
n'th character of a string. It is tempting to fill that gap with a custom
extension like you did, but the problem is that
self[self.startIndex.advancedBy(index)]
is a \$ O(index) \$ operation. In contrast to other languages where a
"character" is a fixed-size object (and accessing the n'th character of
a string a \$ O(1) \$ operation), a Swift Character
represents a
"Unicode grapheme cluster" which consists of one or more Unicode scalar
values, see e.g. Strings in Swift 2 in the Swift blog.
That makes
for i in 0...s.characters.count-1 {
// access `s[i]` with your custom subscript method
}
a O(n^2)
operation where n
is the number of characters in the string.
It is much more efficient to iterate over the characters with
for c in s.characters {
// do something with `c`
}
More remarks:
The explicit type annotations in
var str:String = ""
var longest:Int = Int.min
are not necessary, the compiler can infer the type automatically.
If you start with var longest = 0
then the special case
if s.isEmpty
{
return 0
}
becomes obsolete.
There are actually two errors in your code. If a repeating
character is found then the new substring candidate consists of
that character, i.e.
longest = max(longest, str.characters.count)
str = ""
should be
longest = max(longest, str.characters.count)
str = String(s[i])
otherwise lengthOfLongestSubstring("aabcdd")
returns 3
instead
of 4
.
And the longest substring length must also be updated at the end
of the string:
longest = max(longest, str.characters.count)
return longest
otherwise lengthOfLongestSubstring("aabcd")
returns 1
instead
of 4
.
Putting all that together, the method becomes
func lengthOfLongestSubstring(s: String) -> Int {
var str = ""
var longest = 0
for c in s.characters {
if !str.characters.contains(c) {
str.append(c)
} else {
longest = max(longest, str.characters.count)
str = String(c)
}
}
longest = max(longest, str.characters.count)
return longest
}
which should be faster than your code.
Another possible improvement is to store the characters of the
current substring in an Array
or a Set
instead of a String
.
Which one is faster depends on the size of the strings, here is
an example with an array:
func lengthOfLongestSubstring(s: String) -> Int {
var substringChars: [Character] = []
var longest = 0
for c in s.characters {
if !substringChars.contains(c) {
substringChars.append(c)
} else {
longest = max(longest, substringChars.count)
substringChars = [c]
}
}
longest = max(longest, substringChars.count)
return longest
}
As of Swift 4, a Swift string is a collection of its characters
again, so for c in s.characters
can be simplified to for c in s
.