From 22f08bafafd75b376779235be6888cfe68148337 Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Wed, 1 Apr 2020 17:06:02 +0300 Subject: [PATCH 1/8] Fixed inconsistencies in Horspool version In Horspool version we need skip ahead full pattern length (or less if character already met) when it not full match pattern So in Horspool version we need need correctly fill length of skip of last character in pattern --- Boyer-Moore-Horspool/BoyerMooreHorspool.swift | 134 ++++++++++-------- 1 file changed, 71 insertions(+), 63 deletions(-) diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift index 481b7d483..68e7c6b66 100644 --- a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift @@ -6,70 +6,78 @@ http://www.drdobbs.com/database/faster-string-searches/184408171 */ extension String { - func index(of pattern: String, usingHorspoolImprovement: Bool = false) -> Index? { - // Cache the length of the search pattern because we're going to - // use it a few times and it's expensive to calculate. - let patternLength = pattern.count - guard patternLength > 0, patternLength <= self.count else { return nil } - - // Make the skip table. This table determines how far we skip ahead - // when a character from the pattern is found. - var skipTable = [Character: Int]() - for (i, c) in pattern.enumerated() { - skipTable[c] = patternLength - i - 1 - } - - // This points at the last character in the pattern. - let p = pattern.index(before: pattern.endIndex) - let lastChar = pattern[p] - - // The pattern is scanned right-to-left, so skip ahead in the string by - // the length of the pattern. (Minus 1 because startIndex already points - // at the first character in the source string.) - var i = index(startIndex, offsetBy: patternLength - 1) - - // This is a helper function that steps backwards through both strings - // until we find a character that doesn’t match, or until we’ve reached - // the beginning of the pattern. - func backwards() -> Index? { - var q = p - var j = i - while q > pattern.startIndex { - j = index(before: j) - q = index(before: q) - if self[j] != pattern[q] { return nil } - } - return j + func index(of pattern: String, usingHorspoolImprovement: Bool = false) -> Index? { + // Cache the length of the search pattern because we're going to + // use it a few times and it's expensive to calculate. + let patternLength = pattern.count + guard patternLength > 0, patternLength <= self.count else { return nil } + + // This points at the last character in the pattern. + let p = pattern.index(before: pattern.endIndex) + let lastChar = pattern[p] + + // Make the skip table. This table determines how far we skip ahead + // when a character from the pattern is found. + var skipTable = [Character: Int]() + for (i, c) in pattern.enumerated() { + // In Horspool version we gonna skip ahead full pattern length + // when it's last character from the pattern + if usingHorspoolImprovement, i == (patternLength - 1) { + if skipTable[c] == nil { + skipTable[c] = patternLength } - - // The main loop. Keep going until the end of the string is reached. - while i < endIndex { - let c = self[i] - - // Does the current character match the last character from the pattern? - if c == lastChar { - - // There is a possible match. Do a brute-force search backwards. - if let k = backwards() { return k } - - if !usingHorspoolImprovement { - // If no match, we can only safely skip one character ahead. - i = index(after: i) - } else { - // Ensure to jump at least one character (this is needed because the first - // character is in the skipTable, and `skipTable[lastChar] = 0`) - let jumpOffset = max(skipTable[c] ?? patternLength, 1) - i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex - } - } else { - // The characters are not equal, so skip ahead. The amount to skip is - // determined by the skip table. If the character is not present in the - // pattern, we can skip ahead by the full pattern length. However, if - // the character *is* present in the pattern, there may be a match up - // ahead and we can't skip as far. - i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex - } + } else { + skipTable[c] = patternLength - i - 1 + } + } + + // The pattern is scanned right-to-left, so skip ahead in the string by + // the length of the pattern. (Minus 1 because startIndex already points + // at the first character in the source string.) + var i = index(startIndex, offsetBy: patternLength - 1) + + // This is a helper function that steps backwards through both strings + // until we find a character that doesn’t match, or until we’ve reached + // the beginning of the pattern. + func backwards() -> Index? { + var q = p + var j = i + while q > pattern.startIndex { + j = index(before: j) + q = index(before: q) + if self[j] != pattern[q] { return nil } + } + return j + } + + // The main loop. Keep going until the end of the string is reached. + while i < endIndex { + let c = self[i] + + // Does the current character match the last character from the pattern? + if c == lastChar { + + // There is a possible match. Do a brute-force search backwards. + if let k = backwards() { return k } + + if !usingHorspoolImprovement { + // If no match, we can only safely skip one character ahead. + i = index(after: i) + } else { + // Ensure to jump at least one character (this is needed because the first + // character is in the skipTable, and `skipTable[lastChar] = 0`) + let jumpOffset = skipTable[c] ?? patternLength + i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex } - return nil + } else { + // The characters are not equal, so skip ahead. The amount to skip is + // determined by the skip table. If the character is not present in the + // pattern, we can skip ahead by the full pattern length. However, if + // the character *is* present in the pattern, there may be a match up + // ahead and we can't skip as far. + i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex + } } + return nil + } } From a564e32088e96bb4f53e02658cf88c77afba1537 Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Wed, 1 Apr 2020 17:11:12 +0300 Subject: [PATCH 2/8] Delete unnecessary comment --- Boyer-Moore-Horspool/BoyerMooreHorspool.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift index 68e7c6b66..dc50ebe6e 100644 --- a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift @@ -64,8 +64,6 @@ extension String { // If no match, we can only safely skip one character ahead. i = index(after: i) } else { - // Ensure to jump at least one character (this is needed because the first - // character is in the skipTable, and `skipTable[lastChar] = 0`) let jumpOffset = skipTable[c] ?? patternLength i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex } From 52fe0199a63b77c0e87208748a100dd1a92c7faa Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Wed, 1 Apr 2020 17:27:53 +0300 Subject: [PATCH 3/8] Little simplified code and update to Swift 5.1 --- Knuth-Morris-Pratt/KnuthMorrisPratt.swift | 94 +++++++++++++---------- 1 file changed, 52 insertions(+), 42 deletions(-) diff --git a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift index 81bc65278..87dc1d080 100644 --- a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift +++ b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift @@ -8,58 +8,68 @@ import Foundation -extension String { +func indents(_ pattern: String) -> [Int]? { + guard pattern.count > 0 else { return nil } + let start = pattern.startIndex + let length = pattern.count - func indexesOf(ptnr: String) -> [Int]? { - - let text = Array(self.characters) - let pattern = Array(ptnr.characters) - - let textLength: Int = text.count - let patternLength: Int = pattern.count + var indents = Array(repeating: 0, count: length) + var i = 1 + var j = 0 + + while i < length { + let pi = pattern.index(start, offsetBy: i) + let pj = pattern.index(start, offsetBy: j) - guard patternLength > 0 else { - return nil + if pattern[pi] == pattern[pj] { + indents[i] = j + 1 + i += 1 + j += 1 + + } else if j == 0 { + indents[i] = 0 + i += 1 + + } else { + j = indents[j - 1] } + } + + return indents +} + +extension String { + + func indices(of pattern: String) -> [Index] { + guard let indents = indents(pattern) else { return [] } - var suffixPrefix: [Int] = [Int](repeating: 0, count: patternLength) - var textIndex: Int = 0 - var patternIndex: Int = 0 - var indexes: [Int] = [Int]() - - /* Pre-processing stage: computing the table for the shifts (through Z-Algorithm) */ - let zeta = ZetaAlgorithm(ptnr: ptnr) - - for patternIndex in (1 ..< patternLength).reversed() { - textIndex = patternIndex + zeta![patternIndex] - 1 - suffixPrefix[textIndex] = zeta![patternIndex] - } + var indices = [Index]() - /* Search stage: scanning the text for pattern matching */ - textIndex = 0 - patternIndex = 0 + let pLength = pattern.count + let sLength = count + var k = 0 + var l = 0 - while textIndex + (patternLength - patternIndex - 1) < textLength { + while k < sLength { + let sk = index(startIndex, offsetBy: k) + let pl = pattern.index(pattern.startIndex, offsetBy: l) - while patternIndex < patternLength && text[textIndex] == pattern[patternIndex] { - textIndex = textIndex + 1 - patternIndex = patternIndex + 1 - } - - if patternIndex == patternLength { - indexes.append(textIndex - patternIndex) - } - - if patternIndex == 0 { - textIndex = textIndex + 1 + if self[sk] == pattern[pl] { + k += 1 + l += 1 + if l == pLength { + let index = self.index(sk, offsetBy: -(pLength - 1)) + indices.append(index) + l = 0 + } + + } else if l == 0 { + k += 1 } else { - patternIndex = suffixPrefix[patternIndex - 1] + l = indents[l - 1] } } - guard !indexes.isEmpty else { - return nil - } - return indexes + return indices } } From 64bf0899e117642bb13991079f40e660ba991b30 Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Wed, 1 Apr 2020 17:42:48 +0300 Subject: [PATCH 4/8] A bit simplified code and update to Swift 5.1 At Swift 5.1 we can use built in hash function, so there is no need to use custom realisation We just need to use overflow operators to not overflow Int All code the same except hash function --- Rabin-Karp/rabin-karp.swift | 124 +++++++++++++----------------------- 1 file changed, 46 insertions(+), 78 deletions(-) diff --git a/Rabin-Karp/rabin-karp.swift b/Rabin-Karp/rabin-karp.swift index c337de1a7..48c03d8be 100644 --- a/Rabin-Karp/rabin-karp.swift +++ b/Rabin-Karp/rabin-karp.swift @@ -20,95 +20,63 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. -struct Constants { - static let hashMultiplier = 69069 +public func hash(_ string: String) -> Int { + return string.reduce(0) { (result: Int, character: Character) in + result &+ character.hashValue + } } -precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } -infix operator ** : PowerPrecedence -func ** (radix: Int, power: Int) -> Int { - return Int(pow(Double(radix), Double(power))) +public func nextHash(prevHash: Int, dropped: Character, added: Character) -> Int { + return prevHash &- dropped.hashValue &+ added.hashValue } -func ** (radix: Double, power: Int) -> Double { - return pow(radix, Double(power)) -} - -extension Character { - var asInt: Int { - let s = String(self).unicodeScalars - return Int(s[s.startIndex].value) - } -} - -// Find first position of pattern in the text using Rabin Karp algorithm -public func search(text: String, pattern: String) -> Int { - // convert to array of ints - let patternArray = pattern.characters.flatMap { $0.asInt } - let textArray = text.characters.flatMap { $0.asInt } - - if textArray.count < patternArray.count { - return -1 - } - - let patternHash = hash(array: patternArray) - var endIdx = patternArray.count - 1 - let firstChars = Array(textArray[0...endIdx]) - let firstHash = hash(array: firstChars) - - if patternHash == firstHash { - // Verify this was not a hash collison - if firstChars == patternArray { - return 0 - } - } - - var prevHash = firstHash - // Now slide the window across the text to be searched - for idx in 1...(textArray.count - patternArray.count) { - endIdx = idx + (patternArray.count - 1) - let window = Array(textArray[idx...endIdx]) - let windowHash = nextHash( - prevHash: prevHash, - dropped: textArray[idx - 1], - added: textArray[endIdx], - patternSize: patternArray.count - 1 - ) - - if windowHash == patternHash { - if patternArray == window { - return idx - } - } - prevHash = windowHash +public func search(text: String, pattern: String) -> [String.Index] { + if text.count < pattern.count { return [] } + + var indices = [String.Index]() + + let patternHash = hash(pattern) + let patternLength = pattern.count - 1 + let offset = text.index(text.startIndex, offsetBy: patternLength) + let firstText = String(text[...offset]) + let firstHash = hash(firstText) + + if patternHash == firstHash { + if firstText == pattern { + indices.append(text.startIndex) } - - return -1 -} - -public func hash(array: Array) -> Double { - var total: Double = 0 - var exponent = array.count - 1 - for i in array { - total += Double(i) * (Double(Constants.hashMultiplier) ** exponent) - exponent -= 1 + } + + let start = text.index(after: text.startIndex) + let end = text.index(text.endIndex, offsetBy: -patternLength) + var prevHash = firstHash + var i = start + + while i != end { + let terminator = text.index(i, offsetBy: patternLength) + let window = text[i...terminator] + let prev = text.index(before: i) + let windowHash = nextHash(prevHash: prevHash, dropped: text[prev], added: text[terminator]) + + if windowHash == patternHash, + pattern == window { + indices.append(i) } - - return Double(total) -} - -public func nextHash(prevHash: Double, dropped: Int, added: Int, patternSize: Int) -> Double { - let oldHash = prevHash - (Double(dropped) * - (Double(Constants.hashMultiplier) ** patternSize)) - return Double(Constants.hashMultiplier) * oldHash + Double(added) + + prevHash = windowHash + + i = text.index(after: i) + } + + return indices } // TESTS assert(search(text:"The big dog jumped over the fox", - pattern:"ump") == 13, "Invalid index returned") + pattern:"ump") == 13, "Invalid index returned") assert(search(text:"The big dog jumped over the fox", - pattern:"missed") == -1, "Invalid index returned") + pattern:"missed") == -1, "Invalid index returned") assert(search(text:"The big dog jumped over the fox", - pattern:"T") == 0, "Invalid index returned") + pattern:"T") == 0, "Invalid index returned") From 6be1725a9d7e7f4c387550b2471f931f9ac4f34b Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Wed, 1 Apr 2020 22:39:43 +0300 Subject: [PATCH 5/8] Reduced count of expensive operations --- Knuth-Morris-Pratt/KnuthMorrisPratt.swift | 25 ++++++++--------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift index 87dc1d080..d46d67221 100644 --- a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift +++ b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift @@ -8,9 +8,8 @@ import Foundation -func indents(_ pattern: String) -> [Int]? { +func indents(_ pattern: [String.Element]) -> [Int]? { guard pattern.count > 0 else { return nil } - let start = pattern.startIndex let length = pattern.count var indents = Array(repeating: 0, count: length) @@ -18,10 +17,7 @@ func indents(_ pattern: String) -> [Int]? { var j = 0 while i < length { - let pi = pattern.index(start, offsetBy: i) - let pj = pattern.index(start, offsetBy: j) - - if pattern[pi] == pattern[pj] { + if pattern[i] == pattern[j] { indents[i] = j + 1 i += 1 j += 1 @@ -41,24 +37,21 @@ func indents(_ pattern: String) -> [Int]? { extension String { func indices(of pattern: String) -> [Index] { - guard let indents = indents(pattern) else { return [] } + let ptrn = Array(pattern) + let str = Array(self) + guard let indents = indents(ptrn) else { return [] } var indices = [Index]() - - let pLength = pattern.count - let sLength = count var k = 0 var l = 0 - while k < sLength { - let sk = index(startIndex, offsetBy: k) - let pl = pattern.index(pattern.startIndex, offsetBy: l) + while k < str.count { - if self[sk] == pattern[pl] { + if str[k] == ptrn[l] { k += 1 l += 1 - if l == pLength { - let index = self.index(sk, offsetBy: -(pLength - 1)) + if l == ptrn.count { + let index = self.index(startIndex, offsetBy: k - ptrn.count) indices.append(index) l = 0 } From de6a944e0616394a963dccbdc94ce8fa8fc1f65a Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Thu, 2 Apr 2020 01:42:00 +0300 Subject: [PATCH 6/8] four space indentation --- Boyer-Moore-Horspool/BoyerMooreHorspool.swift | 134 +++++++++--------- 1 file changed, 67 insertions(+), 67 deletions(-) diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift index dc50ebe6e..4ddd1b52e 100644 --- a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift @@ -6,76 +6,76 @@ http://www.drdobbs.com/database/faster-string-searches/184408171 */ extension String { - func index(of pattern: String, usingHorspoolImprovement: Bool = false) -> Index? { - // Cache the length of the search pattern because we're going to - // use it a few times and it's expensive to calculate. - let patternLength = pattern.count - guard patternLength > 0, patternLength <= self.count else { return nil } - - // This points at the last character in the pattern. - let p = pattern.index(before: pattern.endIndex) - let lastChar = pattern[p] - - // Make the skip table. This table determines how far we skip ahead - // when a character from the pattern is found. - var skipTable = [Character: Int]() - for (i, c) in pattern.enumerated() { - // In Horspool version we gonna skip ahead full pattern length - // when it's last character from the pattern - if usingHorspoolImprovement, i == (patternLength - 1) { - if skipTable[c] == nil { - skipTable[c] = patternLength + func index(of pattern: String, usingHorspoolImprovement: Bool = false) -> Index? { + // Cache the length of the search pattern because we're going to + // use it a few times and it's expensive to calculate. + let patternLength = pattern.count + guard patternLength > 0, patternLength <= self.count else { return nil } + + // This points at the last character in the pattern. + let p = pattern.index(before: pattern.endIndex) + let lastChar = pattern[p] + + // Make the skip table. This table determines how far we skip ahead + // when a character from the pattern is found. + var skipTable = [Character: Int]() + for (i, c) in pattern.enumerated() { + // In Horspool version we gonna skip ahead full pattern length + // when it's last character from the pattern + if usingHorspoolImprovement, i == (patternLength - 1) { + if skipTable[c] == nil { + skipTable[c] = patternLength + } + } else { + skipTable[c] = patternLength - i - 1 + } } - } else { - skipTable[c] = patternLength - i - 1 - } - } - - // The pattern is scanned right-to-left, so skip ahead in the string by - // the length of the pattern. (Minus 1 because startIndex already points - // at the first character in the source string.) - var i = index(startIndex, offsetBy: patternLength - 1) - - // This is a helper function that steps backwards through both strings - // until we find a character that doesn’t match, or until we’ve reached - // the beginning of the pattern. - func backwards() -> Index? { - var q = p - var j = i - while q > pattern.startIndex { - j = index(before: j) - q = index(before: q) - if self[j] != pattern[q] { return nil } - } - return j - } - - // The main loop. Keep going until the end of the string is reached. - while i < endIndex { - let c = self[i] - - // Does the current character match the last character from the pattern? - if c == lastChar { - // There is a possible match. Do a brute-force search backwards. - if let k = backwards() { return k } + // The pattern is scanned right-to-left, so skip ahead in the string by + // the length of the pattern. (Minus 1 because startIndex already points + // at the first character in the source string.) + var i = index(startIndex, offsetBy: patternLength - 1) + + // This is a helper function that steps backwards through both strings + // until we find a character that doesn’t match, or until we’ve reached + // the beginning of the pattern. + func backwards() -> Index? { + var q = p + var j = i + while q > pattern.startIndex { + j = index(before: j) + q = index(before: q) + if self[j] != pattern[q] { return nil } + } + return j + } - if !usingHorspoolImprovement { - // If no match, we can only safely skip one character ahead. - i = index(after: i) - } else { - let jumpOffset = skipTable[c] ?? patternLength - i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex + // The main loop. Keep going until the end of the string is reached. + while i < endIndex { + let c = self[i] + + // Does the current character match the last character from the pattern? + if c == lastChar { + + // There is a possible match. Do a brute-force search backwards. + if let k = backwards() { return k } + + if !usingHorspoolImprovement { + // If no match, we can only safely skip one character ahead. + i = index(after: i) + } else { + let jumpOffset = skipTable[c] ?? patternLength + i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex + } + } else { + // The characters are not equal, so skip ahead. The amount to skip is + // determined by the skip table. If the character is not present in the + // pattern, we can skip ahead by the full pattern length. However, if + // the character *is* present in the pattern, there may be a match up + // ahead and we can't skip as far. + i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex + } } - } else { - // The characters are not equal, so skip ahead. The amount to skip is - // determined by the skip table. If the character is not present in the - // pattern, we can skip ahead by the full pattern length. However, if - // the character *is* present in the pattern, there may be a match up - // ahead and we can't skip as far. - i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex - } + return nil } - return nil - } } From 5f7bacd0cb91fb1ab5995854b67e94e10a143494 Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Thu, 2 Apr 2020 01:45:14 +0300 Subject: [PATCH 7/8] four space indentation --- Knuth-Morris-Pratt/KnuthMorrisPratt.swift | 96 +++++++++++------------ 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift index d46d67221..4e20e2eec 100644 --- a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift +++ b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift @@ -9,60 +9,60 @@ import Foundation func indents(_ pattern: [String.Element]) -> [Int]? { - guard pattern.count > 0 else { return nil } - let length = pattern.count - - var indents = Array(repeating: 0, count: length) - var i = 1 - var j = 0 - - while i < length { - if pattern[i] == pattern[j] { - indents[i] = j + 1 - i += 1 - j += 1 - - } else if j == 0 { - indents[i] = 0 - i += 1 - - } else { - j = indents[j - 1] + guard pattern.count > 0 else { return nil } + let length = pattern.count + + var indents = Array(repeating: 0, count: length) + var i = 1 + var j = 0 + + while i < length { + if pattern[i] == pattern[j] { + indents[i] = j + 1 + i += 1 + j += 1 + + } else if j == 0 { + indents[i] = 0 + i += 1 + + } else { + j = indents[j - 1] + } } - } - - return indents + + return indents } extension String { - - func indices(of pattern: String) -> [Index] { - let ptrn = Array(pattern) - let str = Array(self) - guard let indents = indents(ptrn) else { return [] } - var indices = [Index]() - var k = 0 - var l = 0 - - while k < str.count { - - if str[k] == ptrn[l] { - k += 1 - l += 1 - if l == ptrn.count { - let index = self.index(startIndex, offsetBy: k - ptrn.count) - indices.append(index) - l = 0 + func indices(of pattern: String) -> [Index] { + let ptrn = Array(pattern) + let str = Array(self) + guard let indents = indents(ptrn) else { return [] } + + var indices = [Index]() + var k = 0 + var l = 0 + + while k < str.count { + + if str[k] == ptrn[l] { + k += 1 + l += 1 + if l == ptrn.count { + let index = self.index(startIndex, offsetBy: k - ptrn.count) + indices.append(index) + l = 0 + } + + } else if l == 0 { + k += 1 + } else { + l = indents[l - 1] + } } - } else if l == 0 { - k += 1 - } else { - l = indents[l - 1] - } + return indices } - - return indices - } } From 20f224436983078568ea35a0c8eabc2f1819b8ed Mon Sep 17 00:00:00 2001 From: Nikita <47992492+Kn1kt@users.noreply.github.com> Date: Thu, 2 Apr 2020 01:46:46 +0300 Subject: [PATCH 8/8] four space indentation --- Rabin-Karp/rabin-karp.swift | 76 ++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Rabin-Karp/rabin-karp.swift b/Rabin-Karp/rabin-karp.swift index 48c03d8be..2aafb5f75 100644 --- a/Rabin-Karp/rabin-karp.swift +++ b/Rabin-Karp/rabin-karp.swift @@ -21,54 +21,54 @@ // SOFTWARE. public func hash(_ string: String) -> Int { - return string.reduce(0) { (result: Int, character: Character) in - result &+ character.hashValue - } + return string.reduce(0) { (result: Int, character: Character) in + result &+ character.hashValue + } } public func nextHash(prevHash: Int, dropped: Character, added: Character) -> Int { - return prevHash &- dropped.hashValue &+ added.hashValue + return prevHash &- dropped.hashValue &+ added.hashValue } public func search(text: String, pattern: String) -> [String.Index] { - if text.count < pattern.count { return [] } - - var indices = [String.Index]() - - let patternHash = hash(pattern) - let patternLength = pattern.count - 1 - let offset = text.index(text.startIndex, offsetBy: patternLength) - let firstText = String(text[...offset]) - let firstHash = hash(firstText) - - if patternHash == firstHash { - if firstText == pattern { - indices.append(text.startIndex) - } - } - - let start = text.index(after: text.startIndex) - let end = text.index(text.endIndex, offsetBy: -patternLength) - var prevHash = firstHash - var i = start - - while i != end { - let terminator = text.index(i, offsetBy: patternLength) - let window = text[i...terminator] - let prev = text.index(before: i) - let windowHash = nextHash(prevHash: prevHash, dropped: text[prev], added: text[terminator]) + if text.count < pattern.count { return [] } + + var indices = [String.Index]() - if windowHash == patternHash, - pattern == window { - indices.append(i) + let patternHash = hash(pattern) + let patternLength = pattern.count - 1 + let offset = text.index(text.startIndex, offsetBy: patternLength) + let firstText = String(text[...offset]) + let firstHash = hash(firstText) + + if patternHash == firstHash { + if firstText == pattern { + indices.append(text.startIndex) + } } - prevHash = windowHash + let start = text.index(after: text.startIndex) + let end = text.index(text.endIndex, offsetBy: -patternLength) + var prevHash = firstHash + var i = start + + while i != end { + let terminator = text.index(i, offsetBy: patternLength) + let window = text[i...terminator] + let prev = text.index(before: i) + let windowHash = nextHash(prevHash: prevHash, dropped: text[prev], added: text[terminator]) + + if windowHash == patternHash, + pattern == window { + indices.append(i) + } + + prevHash = windowHash + + i = text.index(after: i) + } - i = text.index(after: i) - } - - return indices + return indices } // TESTS