Skip to main content
Code Review

Return to Answer

Fixed typo.
Source Link
Martin R
  • 24.2k
  • 2
  • 37
  • 95

Arrays are RandomAccessCollections and determining their count is a \$ O(a) \$\$ O(1) \$ operation. That should already improve the performance considerably for large strings.

Arrays are RandomAccessCollections and determining their count is a \$ O(a) \$ operation. That should already improve the performance considerably for large strings.

Arrays are RandomAccessCollections and determining their count is a \$ O(1) \$ operation. That should already improve the performance considerably for large strings.

added 250 characters in body
Source Link
Martin R
  • 24.2k
  • 2
  • 37
  • 95

Try to use more descriptive variable names (what is var distinct) or at least document their meaning.

  • Try to use more descriptive variable names (what is var distinct) or at least document their meaning.

  • Don't abbreviate variable names: use e.g. length instead of len, or startIndex instead of start.

  • Determining the number of key/value entries in a dictionary can be simplified to

     var distinct = freq.count
    

Don't abbreviate variable names: use e.g. length instead of len, or startIndex instead of startfreq.keys.count.

  • The parentheses at

     while (distinct == 0) { ... }
    

are not needed.

Try to use more descriptive variable names (what is var distinct) or at least document their meaning.

Don't abbreviate variable names: use e.g. length instead of len, or startIndex instead of start.

  • Try to use more descriptive variable names (what is var distinct) or at least document their meaning.

  • Don't abbreviate variable names: use e.g. length instead of len, or startIndex instead of start.

  • Determining the number of key/value entries in a dictionary can be simplified to

     var distinct = freq.count
    

instead of freq.keys.count.

  • The parentheses at

     while (distinct == 0) { ... }
    

are not needed.

added 4657 characters in body
Source Link
Martin R
  • 24.2k
  • 2
  • 37
  • 95

Performance

The main culprit for exceeding the exceeded time limit is here:

Some simplifications

Some more remarks: Testing for an empty string can be done with isEmpty:

and that can be further shorted using reduce(into:):

var freq = t.reduce(into: [:]) { dict, char in
 dict[char, default: 0] += 1
}

Use Optional instead of magic values

Here

var resLen = Int.max

you are using a "magic value": Int.max indicates that no matching window has been found so far. That works because the given strings are unlikely to have \$ 2^{63} - 1 \$ characters, but it forces you to use exactly the same "magic value" at

if resLen == Int.max { ... }

Also the initial value of

var resStart = 0

is meaningless, it will be overwritten as soon as the first matching window is found.

Magic values are frowned upon in Swift because there is a dedicated language feature for the purpose: the optional values.

var resLen: Int?

clearly indicates an undefined value, and later be tested with optional binding.

A (minor) drawback is that you no longer simply compare

if resLen > end - start { ... }

but I'll come back to that later.

Use structs to combine related properties

Both the current and the best window are described by two properties (

var end = 0
var start = 0
var resStart = 0
var resLen = Int.max

or – with the above suggestion – by three properties

var start = s.startIndex // Start of current window
var end = s.startIndex // End of current window
var len = 0 // Length of current window
// Similar for best window ...

With a

struct StringWindow {
 var startIndex: String.Index
 var endIndex: String.Index
 var length: Int
 
 // ...
}

these related properties are nicely combined, and it makes the code more self-documenting:

var currentWindow = StringWindow(...)
var bestWindow: StringWindow?

Comparing the length against the optional bestWindow can be put into a method of that type, assigning a new best window is simply done with

bestWindow = currentWindow

and the final result can be determined with optional binding:

if let best = bestWindow {
 return String(s[best.startIndex..<best.endIndex])
} else {
 return ""
}

Putting it together

Putting all the above suggestions together the code could look like this:

func minWindow(_ s: String, _ t: String) -> String {
 
 struct StringWindow {
 var startIndex: String.Index
 var endIndex: String.Index
 var length: Int
 
 init(for string: String) {
 self.startIndex = string.startIndex
 self.endIndex = string.startIndex
 self.length = 0
 }
 
 func shorter(than other: StringWindow?) -> Bool {
 if let other = other {
 return length < other.length
 } else {
 return true
 }
 }
 }
 if s.isEmpty || t.isEmpty || s.count < t.count {
 return ""
 }
 
 var freq = t.reduce(into: [:]) { dict, char in
 dict[char, default: 0] += 1
}
 var distinct = freq.count
 var currentWindow = StringWindow(for: s)
 var bestWindow: StringWindow?
 
 while currentWindow.endIndex != s.endIndex {
 let curChar = s[currentWindow.endIndex]
 s.formIndex(after: &currentWindow.endIndex)
 currentWindow.length += 1
 
 if let val = freq[curChar] {
 freq[curChar] = val - 1
 if val - 1 == 0 {
 distinct -= 1
 }
 }
 
 while distinct == 0 {
 if currentWindow.shorter(than: bestWindow) {
 bestWindow = currentWindow
 }
 
 let curStart = s[currentWindow.startIndex]
 if let val = freq[curStart] {
 freq[curStart] = val + 1
 if val + 1 > 0 {
 distinct += 1
 }
 }
 s.formIndex(after: &currentWindow.startIndex)
 currentWindow.length -= 1
 }
 }
 
 if let best = bestWindow {
 return String(s[best.startIndex..<best.endIndex])
 } else {
 return ""
 }
}

Final remarks

Try to use more descriptive variable names (what is var distinct) or at least document their meaning.

Don't abbreviate variable names: use e.g. length instead of len, or startIndex instead of start.

The main culprit for the exceeded time limit is here:

Some more remarks: Testing for an empty string can be done with isEmpty:

and that can be further shorted using reduce(into:):

var freq = t.reduce(into: [:]) { dict, char in
 dict[char, default: 0] += 1
}

Performance

The main culprit for exceeding the time limit is here:

Some simplifications

Testing for an empty string can be done with isEmpty:

and that can be further shorted using reduce(into:):

var freq = t.reduce(into: [:]) { dict, char in
 dict[char, default: 0] += 1
}

Use Optional instead of magic values

Here

var resLen = Int.max

you are using a "magic value": Int.max indicates that no matching window has been found so far. That works because the given strings are unlikely to have \$ 2^{63} - 1 \$ characters, but it forces you to use exactly the same "magic value" at

if resLen == Int.max { ... }

Also the initial value of

var resStart = 0

is meaningless, it will be overwritten as soon as the first matching window is found.

Magic values are frowned upon in Swift because there is a dedicated language feature for the purpose: the optional values.

var resLen: Int?

clearly indicates an undefined value, and later be tested with optional binding.

A (minor) drawback is that you no longer simply compare

if resLen > end - start { ... }

but I'll come back to that later.

Use structs to combine related properties

Both the current and the best window are described by two properties (

var end = 0
var start = 0
var resStart = 0
var resLen = Int.max

or – with the above suggestion – by three properties

var start = s.startIndex // Start of current window
var end = s.startIndex // End of current window
var len = 0 // Length of current window
// Similar for best window ...

With a

struct StringWindow {
 var startIndex: String.Index
 var endIndex: String.Index
 var length: Int
 
 // ...
}

these related properties are nicely combined, and it makes the code more self-documenting:

var currentWindow = StringWindow(...)
var bestWindow: StringWindow?

Comparing the length against the optional bestWindow can be put into a method of that type, assigning a new best window is simply done with

bestWindow = currentWindow

and the final result can be determined with optional binding:

if let best = bestWindow {
 return String(s[best.startIndex..<best.endIndex])
} else {
 return ""
}

Putting it together

Putting all the above suggestions together the code could look like this:

func minWindow(_ s: String, _ t: String) -> String {
 
 struct StringWindow {
 var startIndex: String.Index
 var endIndex: String.Index
 var length: Int
 
 init(for string: String) {
 self.startIndex = string.startIndex
 self.endIndex = string.startIndex
 self.length = 0
 }
 
 func shorter(than other: StringWindow?) -> Bool {
 if let other = other {
 return length < other.length
 } else {
 return true
 }
 }
 }
 if s.isEmpty || t.isEmpty || s.count < t.count {
 return ""
 }
 
 var freq = t.reduce(into: [:]) { dict, char in
 dict[char, default: 0] += 1
}
 var distinct = freq.count
 var currentWindow = StringWindow(for: s)
 var bestWindow: StringWindow?
 
 while currentWindow.endIndex != s.endIndex {
 let curChar = s[currentWindow.endIndex]
 s.formIndex(after: &currentWindow.endIndex)
 currentWindow.length += 1
 
 if let val = freq[curChar] {
 freq[curChar] = val - 1
 if val - 1 == 0 {
 distinct -= 1
 }
 }
 
 while distinct == 0 {
 if currentWindow.shorter(than: bestWindow) {
 bestWindow = currentWindow
 }
 
 let curStart = s[currentWindow.startIndex]
 if let val = freq[curStart] {
 freq[curStart] = val + 1
 if val + 1 > 0 {
 distinct += 1
 }
 }
 s.formIndex(after: &currentWindow.startIndex)
 currentWindow.length -= 1
 }
 }
 
 if let best = bestWindow {
 return String(s[best.startIndex..<best.endIndex])
 } else {
 return ""
 }
}

Final remarks

Try to use more descriptive variable names (what is var distinct) or at least document their meaning.

Don't abbreviate variable names: use e.g. length instead of len, or startIndex instead of start.

added 182 characters in body
Source Link
Martin R
  • 24.2k
  • 2
  • 37
  • 95
Loading
Source Link
Martin R
  • 24.2k
  • 2
  • 37
  • 95
Loading
default

AltStyle によって変換されたページ (->オリジナル) /