How to solve a problem with using the method of branches and borders? - swift

All words of the ternary language consist of only 3 letters: a, b, and c and all have a strictly specified length N. Words that do not contain two identical subsequences of letters in a row are considered correct. For example, abcacb is the correct word, and ababc is not the correct one, since the ab subsequences go there.
I tried to solve the problem with a complete enumeration of all possible combinations and a function that looked for a repeating sequence. However, this turned out to be the wrong decision. The problem needs to be solved somehow using the branch and bound method. I have absolutely no idea how this problem can be solved by this method. I would be very happy if someone provides examples or explains to me. I have already spent six days to solve this problem and am very tired.
My wrong solution:
import Foundation
func findRepetition(_ p: String) -> [String:Int] {
var repDict: [String:Int] = [:]
var p = p
while p.count != 0 {
for i in 0...p.count-1 {
repDict[String(Array(p)[0..<i]), default: 0] += 1
}
p = String(p.dropFirst())
}
return repDict
}
var correctWords = [String]()
var wrongWords = [String]()
func getRepeats(_ p: String) -> Bool {
let p = p
var a = findRepetition(p)
for i in a {
var substring = String(Array(repeating: i.key, count: 2).joined())
if p.contains(substring) {
wrongWords.append(p)
return false
}
}
correctWords.append(p)
return true
}
var counter = 0
func allLexicographicRecur (_ string: [String.Element], _ data: [String], _ last: Int, _ index: Int){
var length = string.count-1
var data = data
for i in 0...length {
data[index] = String(string[i])
if index == last {
if getRepeats(data.joined()) {
counter += 1
}
}else{
allLexicographicRecur(string, data, last, index+1)
}
}
}
func threeLanguage(_ l: Int) {
var alphabet = "abc"
var data = Array(repeating: "", count: l)
allLexicographicRecur(alphabet.sorted(), data, l-1, 0)
print("The specified word length: \(l), the number of correct words: \(counter)\n")
print("Correct words:\n\(correctWords)\n")
print("Wrong words:\n\(wrongWords)")
}
threeLanguage(3)
Example:
abca is the right word.
abab is wrong (ab).
aaaa is also wrong (a).
abcabc is also incorrect (abc).

If I correctly understood your problem, you need to separate you input string to parts N-length and check parts by your rules. Smth like this
let constant: Int = 3
extension String {
private func components(withLength length: Int) -> [String] {
return stride(from: 0, to: count, by: length).map {
let start = index(startIndex, offsetBy: $0)
let end = index(start, offsetBy: length, limitedBy: endIndex) ?? endIndex
return String(self[start ..< end])
}
}
var numberOfValidWords: Int {
var numberOfIncorrectWords = 0
let length = count - constant
let array = components(withLength: constant)
for component in array {
let computedLength = replacingOccurrences(of: component, with: "").count
if computedLength != length {
print("as is lengths are not equal, this part is met in string several times")
numberOfIncorrectWords += 1
continue
}
}
return array.count - numberOfIncorrectWords
}
}
Hope it will be helpful

Related

CodingBat string_bits problem solved using swit for loop

Question:
Given a string, return a new string made of every other char starting with the first, so "Hello" yields "Hlo".
string_bits('Hello') → 'Hlo'
string_bits('Hi') → 'H'
string_bits('Heeololeo') → 'Hello'
Solution:
func string_bits(userString: String) ->String{
var myString = ""
for(i, v) in userString.enumerated(){
if i % 2 == 0{
myString.append(v)
}
}
return myString
}
Output: Hello
Now my question:
Is there any I can iterate my index any way in swift like object-c, c, or other programming languages does. For instance:
result = ""
# On each iteration, add the substring of the chars 0..i
for i in range(len(str)):
result = result + str[:i+1]
return result
str[:i+1]
Here, I am adding +1 with the current index and getting the index value. How can I do this in swift.
extension Collection {
func everyNthIndex(n: Int) -> UnfoldSequence<Index,Index> {
sequence(state: startIndex) { index in
guard index < endIndex else { return nil }
defer { index = self.index(index, offsetBy: n, limitedBy: endIndex) ?? endIndex }
return index
}
}
}
let alphabet = "abcdefghijklmnopqrstuvwxyz"
for evenIndex in alphabet.everyNthIndex(n: 2) {
print("evenIndex", evenIndex, "char:", alphabet[evenIndex])
}
for oddIndex in alphabet.dropFirst().everyNthIndex(n: 2) {
print("oddIndex", oddIndex, "char:", alphabet[oddIndex])
}
regular approach using while loop:
var index = alphabet.startIndex
while index < alphabet.endIndex {
defer { index = alphabet.index(index, offsetBy: 1) }
print(alphabet[index])
print(index)
}
or enumerating the string indices:
func string_bits(userString: String) -> String {
var myString = ""
for (offset,index) in userString.indices.enumerated() {
if offset.isMultiple(of: 2) {
myString.append(userString[index])
}
}
return myString
}

Find the repeated sequence in the line that go in a row

Given a string of arbitrary length. I need to find 1 subsequences of identical characters that go in a row.
My function (there are two of them, but these are two parts of the same function) turned out to be complex and cumbersome and did not fit because of this. The function I need should be simple and not too long.
Example:
Input : str = "abcabc"
Output : abc
Input : str = "aa"
Output : a
Input : str = "abcbabcb"
Output : abcb
Input : str = "abcbca"
Output : bcbc
Input : str = "cbabc"
Output :
Input : str = "acbabc"
Output :
My unsuccessful function:
func findRepetition(_ p: String) -> [String:Int] {
var repDict: [String:Int] = [:]
var p = p
while p.count != 0 {
for i in 0...p.count-1 {
repDict[String(Array(p)[0..<i]), default: 0] += 1
}
p = String(p.dropFirst())
}
return repDict
}
var correctWords = [String]()
var wrongWords = [String]()
func getRepeats(_ p: String) -> Bool {
let p = p
var a = findRepetition(p)
for i in a {
var substring = String(Array(repeating: i.key, count: 2).joined())
if p.contains(substring) {
wrongWords.append(p)
return false
}
}
correctWords.append(p)
return true
}
I will be very grateful for your help!
Here's a solution using regular expression. I used a capture group that tries to match as many characters as possible such that the whole group repeats at least once.
import Foundation
func findRepetition(_ s: String) -> String? {
if s.isEmpty { return nil }
let pattern = "([a-z]+)\\1+"
let regex = try? NSRegularExpression(pattern: pattern, options: [])
if let match = regex?.firstMatch(in: s, options: [], range:
NSRange(location: 0, length: s.utf16.count)) {
let unitRange = match.range(at: 1)
return (s as NSString).substring(with: unitRange)
}
return nil
}
print(findRepetition("abcabc")) //prints abc
print(findRepetition("aa")) //prints a
print(findRepetition("abcbabcb")) //prints abcb
print(findRepetition("abcbca")) //prints bc
print(findRepetition("cbabc")) //prints nil
print(findRepetition("acbabc")) //prints nil
func findRepetitions(_ p : String) -> [String: Int]{
let half = p.count / 2 + 1
var result : [String : Int] = [:]
for i in 1..<half {
for j in 0...(p.count-i) {
let sub = (p as! NSString).substring(with: NSRange.init(location: j, length: i))
if let val = result[sub] {
result[sub] = val + 1
}else {
result[sub] = 1
}
}
}
return result
}
This is for finding repetitions of possible substrings in your string. Hope it can help
Here is a solution that is based on the Suffix Array Algorithm, that finds the longest substring that is repeated (contiguously):
func longestRepeatedSubstring(_ str: String) -> String {
let sortedSuffixIndices = str.indices.sorted { str[$0...] < str[$1...] }
let lcsArray = [0]
+
sortedSuffixIndices.indices.dropFirst().map { index in
let suffix1 = str[sortedSuffixIndices[index]...]
let suffix2 = str[sortedSuffixIndices[index - 1]...]
let commonPrefix = suffix1.commonPrefix(with: suffix2)
let count = commonPrefix.count
let repeated = suffix1.dropFirst(count).commonPrefix(with: commonPrefix)
return count == repeated.count ? count : 0
}
let maxRepeated = zip(sortedSuffixIndices.indices,lcsArray).max(by: { $0.1 < $1.1 })
if let tuple = maxRepeated, tuple.1 != 0 {
let suffix1 = str[sortedSuffixIndices[tuple.0 - 1]...]
let suffix2 = str[sortedSuffixIndices[tuple.0]...]
let longestRepeatedSubstring = suffix1.commonPrefix(with: suffix2)
return longestRepeatedSubstring
} else {
return ""
}
}
Here is an easy to understand tutorial about such an algorithm.
It works for these examples:
longestRepeatedSubstring("abcabc") //"abc"
longestRepeatedSubstring("aa") //"a"
longestRepeatedSubstring("abcbabcb") //"abcd"
longestRepeatedSubstring("abcbca") //"bcbc"
longestRepeatedSubstring("cbabc") //""
longestRepeatedSubstring("acbabc") //""
As well as these:
longestRepeatedSubstring("a😍ca😍c") //"a😍c"
longestRepeatedSubstring("Ab cdAb cd") //"Ab cd"
longestRepeatedSubstring("aabcbc") //"bc"
Benchmarks
Here is a benchmark that clearly shows that the Suffix Array algorithm is much faster than using a regular expression.
The result is:
Regular expression: 7.2 ms
Suffix Array : 0.1 ms

How to remove duplicate characters from a string in Swift

ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr

How to make a function that swaps 3 characters in a string?

For example, if I had a string RedSox and wanted to change it to SoxRed?
I'm thinking it would be something like :
func swapString (String: String) -> String {
var stringReplaced = String
var result = stringReplaced.Select(x=> x == 'A' ? 'B' : (x=='B' ? "A" : x)).ToArray()
stringReplaced = String(result)
return stringReplaced
}
this function takes the last 3 characters of a string and appends them to the beginning, there are definitely less verbose ways of doing this but it works. it will throw an error if passed a string with < 3 characters.
import UIKit
let string = "RedSox"
func changeString ( _ string : String) -> String {
var characters : Array<Character> = []
for character in string.characters {
characters.append(character)
}
var characters2 : Array<Character> = []
var position = characters.count - 3
while position < characters.count {
characters2.append(characters[position])
position += 1
}
characters.removeLast()
characters.removeLast()
characters.removeLast()
characters2.append(contentsOf: characters)
return (String(characters2))
}
var newString = changeString(string)
print (newString)
Just use the methods which the String class already provides.
It's always a good idea putting these kind of "helper" methods in an extension.
// Define String extension
extension String {
func swappedString(count swapCount: Int) -> String {
guard self.characters.count > swapCount else {
return self
}
let index = self.index(self.endIndex, offsetBy: -swapCount)
let first = self.substring(from: index)
let second = self.substring(to: index)
return first + second
}
}
// Use it
"RedSox".swappedString(count: 3) //= SoxRed

In Swift, how to modify a character in string with subscript?

Like in C, we can simply do
str[i] = str[j]
But how to write the similar logic in swift?
Here is my code, but got error:
Cannot assign through subscript: subscript is get-only
let indexI = targetString.index(targetString.startIndex, offsetBy: i)
let indexJ = targetString.index(targetString.startIndex, offsetBy: j)
targetString[indexI] = targetString[indexJ]
I know it may work by using this method, but it's too inconvenient
replaceSubrange(, with: )
In C, a string (char *) can be treated as an array of characters. In Swift, you can convert the String to an [Character], do the modifications you want, and then convert the [Character] back to String.
For example:
let str = "hello"
var strchars = Array(str)
strchars[0] = strchars[4]
let str2 = String(strchars)
print(str2) // "oello"
This might seem like a lot of work for a single modification, but if you are moving many characters this way, you only have to convert once each direction.
Reverse a String
Here's an example of a simple algorithm to reverse a string. By converting to an array of characters first, this algorithm is similar to the way you might do it in C:
let str = "abcdefg"
var strchars = Array(str)
var start = 0
var end = strchars.count - 1
while start < end {
let temp = strchars[start]
strchars[start] = strchars[end]
strchars[end] = temp
start += 1
end -= 1
}
let str2 = String(strchars)
print(str2) // "gfedcba"
Dealing with String with Swift is major pain in the a**. Unlike most languages I know that treat string as an array of characters, Swift treats strings as collection of extended grapheme clusters and the APIs to access them is really clumsy. Changes are coming in Swift 4 but that manifesto lost me about 10 paragraphs in.
Back to your question... you can replace the character like this:
var targetString = "Hello world"
let i = 0
let j = 1
let indexI = targetString.index(targetString.startIndex, offsetBy: i)
let indexJ = targetString.index(targetString.startIndex, offsetBy: j)
targetString.replaceSubrange(indexI...indexI, with: targetString[indexJ...indexJ])
print(targetString) // eello world
I was quite shocked as well by the fact that swift makes string indexing so damn complicated. For that reason, I have built some string extensions that enable you to retrieve and change parts of strings based on indices, closed ranges, and open ranges, PartialRangeFrom, PartialRangeThrough, and PartialRangeUpTo. You can download the repository I created here
You can also pass in negative numbers in order to access characters from the end backwards.
public extension String {
/**
Enables passing in negative indices to access characters
starting from the end and going backwards.
if num is negative, then it is added to the
length of the string to retrieve the true index.
*/
func negativeIndex(_ num: Int) -> Int {
return num < 0 ? num + self.count : num
}
func strOpenRange(index i: Int) -> Range<String.Index> {
let j = negativeIndex(i)
return strOpenRange(j..<(j + 1), checkNegative: false)
}
func strOpenRange(
_ range: Range<Int>, checkNegative: Bool = true
) -> Range<String.Index> {
var lower = range.lowerBound
var upper = range.upperBound
if checkNegative {
lower = negativeIndex(lower)
upper = negativeIndex(upper)
}
let idx1 = index(self.startIndex, offsetBy: lower)
let idx2 = index(self.startIndex, offsetBy: upper)
return idx1..<idx2
}
func strClosedRange(
_ range: CountableClosedRange<Int>, checkNegative: Bool = true
) -> ClosedRange<String.Index> {
var lower = range.lowerBound
var upper = range.upperBound
if checkNegative {
lower = negativeIndex(lower)
upper = negativeIndex(upper)
}
let start = self.index(self.startIndex, offsetBy: lower)
let end = self.index(start, offsetBy: upper - lower)
return start...end
}
// MARK: - Subscripts
/**
Gets and sets a character at a given index.
Negative indices are added to the length so that
characters can be accessed from the end backwards
Usage: `string[n]`
*/
subscript(_ i: Int) -> String {
get {
return String(self[strOpenRange(index: i)])
}
set {
let range = strOpenRange(index: i)
replaceSubrange(range, with: newValue)
}
}
/**
Gets and sets characters in an open range.
Supports negative indexing.
Usage: `string[n..<n]`
*/
subscript(_ r: Range<Int>) -> String {
get {
return String(self[strOpenRange(r)])
}
set {
replaceSubrange(strOpenRange(r), with: newValue)
}
}
/**
Gets and sets characters in a closed range.
Supports negative indexing
Usage: `string[n...n]`
*/
subscript(_ r: CountableClosedRange<Int>) -> String {
get {
return String(self[strClosedRange(r)])
}
set {
replaceSubrange(strClosedRange(r), with: newValue)
}
}
/// `string[n...]`. See PartialRangeFrom
subscript(r: PartialRangeFrom<Int>) -> String {
get {
return String(self[strOpenRange(r.lowerBound..<self.count)])
}
set {
replaceSubrange(strOpenRange(r.lowerBound..<self.count), with: newValue)
}
}
/// `string[...n]`. See PartialRangeThrough
subscript(r: PartialRangeThrough<Int>) -> String {
get {
let upper = negativeIndex(r.upperBound)
return String(self[strClosedRange(0...upper, checkNegative: false)])
}
set {
let upper = negativeIndex(r.upperBound)
replaceSubrange(
strClosedRange(0...upper, checkNegative: false), with: newValue
)
}
}
/// `string[...<n]`. See PartialRangeUpTo
subscript(r: PartialRangeUpTo<Int>) -> String {
get {
let upper = negativeIndex(r.upperBound)
return String(self[strOpenRange(0..<upper, checkNegative: false)])
}
set {
let upper = negativeIndex(r.upperBound)
replaceSubrange(
strOpenRange(0..<upper, checkNegative: false), with: newValue
)
}
}
}
Usage:
let text = "012345"
print(text[2]) // "2"
print(text[-1] // "5"
print(text[1...3]) // "123"
print(text[2..<3]) // "2"
print(text[3...]) // "345"
print(text[...3]) // "0123"
print(text[..<3]) // "012"
print(text[(-3)...] // "345"
print(text[...(-2)] // "01234"
All of the above works with assignment as well. All subscripts have getters and setters.
a new extension added,
since String conforms to BidirectionalCollection Protocol
extension String{
subscript(at i: Int) -> String? {
get {
if i < count{
let idx = index(startIndex, offsetBy: i)
return String(self[idx])
}
else{
return nil
}
}
set {
if i < count{
let idx = index(startIndex, offsetBy: i)
remove(at: idx)
if let new = newValue, let first = new.first{
insert(first, at: idx)
}
}
}
}
}
call like this:
var str = "fighter"
str[at: 2] = "6"