Как я Sberfight 2022 проходил на Swift

в 14:25, , рубрики: 2022, 2022 год, sberfight, swift

В 2021 году на просторах интернета случайно увидел Sber на geecko.com, тогда компания Sber проводила fight типа "староверы" против "новокодеров". (Простите за неточности, вспоминаю по памяти.)

И когда запустили конкурс Sberfight я уже автоматически попал в рассылку.

Я относительно молод в Swift и тренировка умений или же проверка навыков на скорость очень привлекла. А формат в стиле "Денди" поднимает давно забытое чувство детства. (Моей любимой игрой были "танчики", "контра" и "червяк Джим"- правда у друзей на "Сеге".)

Однако, первый неприятный сюрприз ждал в условиях конкурса - подсчет баллов. Оказывается подсчет баллов теперь осуществляется - 100 баллов за задание(при учете, что все тест кейсы положительные) и + за скорость(по формуле где Swift имеет коэффициент примерно 1.29, как и JS, а вот максимальный С# - 2). Всего 8 заданий - значит минимум 800 баллов без надбавок. А вот сколько за скорость? Ответ был найден быстро - только взглянув на турнирную таблицу (Топ-1 - примерно - 3400 баллов). То есть 2400 надбавка за скорость (предположим C# - значит в общем за Swift при таком же идеальном выполнении я получу - 2477 баллов). Тут-то интерес начал угасать.

А, эскиз победителей уже нарисовался.

Пройдя два задания и поняв, что быстрее отведенного времени я не успеваю, а значит надбавки за скорость нет. Я взглянул на обновленный топ и увидев (Топ-1 - 5500 баллов) моей грусти не было предела.

Так и закрыл я Sberfight до конца февраля, пока мне на почту не упало письмо от рекрутера Sber. (Что это была массовая рассылка - это понятно, но зачем за два задания из 8 для меня осталось вопросом.) Но такая напоминалка заинтриговала меня глянуть на лидеров и каким же было мое удивление, когда Топ 1 - стоял 3400 баллов. Понятно, кто-то нашел баг, накрутил себе баллов, а теперь все пофиксили. Вот тут интерес и разогрелся, увы оставалось два дня.

Однако, пройти все тест кейсы быстрее отведенного времени для меня все равно осталось непреодолимым.

Ради интереса других и комментариев выложу тут свое решение. Увы, из-за спешки забыл скопировать задания и восстанавливаю по памяти.

Тест #1

Задание:

Вывести номера тех чисел, которые будут самыми большими при увеличении их на K

func getResult(cash: [Int], k: Int) -> [Int] {
    var tempArrOfThiefes: [Int] = []
    
    for index in 0..<cash.count {
        print("cash - ", cash[index])
        
        var tempArr: [Int] = []
        for ygrek in 0..<cash.count {
            if (cash[index]+k) > cash[ygrek] {
                tempArr.append(index)
            }
        }
        print("temp arr count - ", tempArr.count)
        if (tempArr.count + 1) >= cash.count {
            print("index appended - ", index)
            tempArrOfThiefes.append(index + 1 )
            tempArr = []
        }
    }
    return tempArrOfThiefes
}

// Test #1
let x = [1,3,4,2]
let k = 2
let res = getResult(cash: x, k: k)
print(res)

Тест #2 (тут задание я так и не вспомнил)


func getResult(nums: [Int], k: Int) -> Int {
    if k == 0 {
        return 0
    }
    var tempInt: Int = 0
    var tempArr = nums
    var tempK = k
    
    repeat {

        for index in 0..<tempArr.count{
            if tempArr[index] <= tempK {
                tempArr[index] = 0
                tempArr.sort()
                tempK += 1
                tempInt += 1
            }
        }

    } while (tempK < tempArr.min()!)
    
    
    return tempInt
}

// Test #1
let x = [1,2,3,4,5]
let k = 1
let res = getResult(nums: x, k: k)
print(res)

Тест #3 (для данного теста на просторах Интернета нашел решение на Python, на которое пытался опираться)

Задание:

Мы знаем количество гостей, ваша задача рассадить всех за стол. Однако, некоторые гости дали вам список неприятелей, с которыми они не сядут. Стулья расставили так, что у стола оказалось два крайних места, у которых только один соседний гость. В остальных случаях соседа два. Определите, можно ли рассадить гостей так, чтобы все оказались довольны.

Ввод:

invited_list - количество приглашённых гостей, 0<invited_list<10

dislike_list - строчный массив неприятелей, ["1-2,3"] - означает, что гость под номером 1 не сядет с гостями 2 и 3

Вывод:

Boolean - возможно ли рассадить гостей так, чтобы они все были довольны

Пример:

invited_list = 4

dislike_list = ["1-2", "3-4"]

get_result(invited_list, dislike_list) = True // [1, 4, 2, 3]

Python (эргономичностью решения я восхитился, источник решения: https://www.cyberforum.ru/python-tasks/thread2943547.html)

def guests_seating( n, dis ):
    lis = list( range(1, n + 1) )
    bad_pairs = set()
    for e in dis:
        L, de, R = e.partition('-')
        not_frends = set( int(x) for x in R.split(',') )
        for nf in not_frends:
            bad_pairs.add( frozenset( [int(L), nf] ) )
    res = [ [x] for x in lis ]
    flag = True
    while flag:
        flag = False
        for x in res:
            for y in lis:
                if frozenset([x[-1],y]) not in bad_pairs and y not in x:
                    x.append(y)
                    flag = True
                    if len(x) == n:
                        #return x
                        return True
    return False
#==============================================================================
n = 10
dis = '1-2,4,6,8 3-5,7,9 4-1,2,3 5-2,4 6-3'.split()
print( guests_seating( n, dis ) )
func getResult(invitedList: Int, dislikeList: [String]) -> Bool {
    if invitedList == 0 {
        return true
    } else if invitedList == 1 {
        return true
    }
    let allGuest = [Int](1...invitedList)
    print("allGuest created - ", allGuest)
    var badPairs:[Int:[Int]] = [:]
    for indexB in 0..<invitedList {
        var tempBadPairs:[Int: [Int]] = [:]
        for indexK in 0..<dislikeList.count {
            let guestArr = dislikeList[indexK].components(separatedBy: "-")
            let keyElement = Int(guestArr.first!)!
            let dislikeElements = guestArr.last!.components(separatedBy: ",")
            let valueElement = dislikeElements.map{Int($0)!}
            tempBadPairs[keyElement] = valueElement
        }
        if tempBadPairs.keys.contains(indexB+1) {
            let newValue = tempBadPairs[indexB+1]
            badPairs[indexB+1] = newValue
        } else {
            badPairs[indexB+1] = [0]
        }
    }
    print("Done well - ", badPairs)
    
    var sortedGuests: [Int] = []
    sortedGuests.append(1)
    var flag = 0
    while flag<invitedList {
        flag += 1
        
        for indexQ in 2...badPairs.count {
            print("x is - ", indexQ)
            if !badPairs[sortedGuests.last!]!.contains(indexQ) {
                if !sortedGuests.contains(indexQ) {
                    sortedGuests.append(indexQ)
                    print("sortedGuests.append(x.key) - ", indexQ)
                }
            }
        }
        
        if sortedGuests.count == invitedList {
            print("sorted guests - ", sortedGuests)
            return true
        }
        print("sorted guests - ", sortedGuests)
    }
    
    return false
}

// Test #1
let invited_list1 = 4
let dislike_list1 = ["1-2,3", "3-4"]
let intT1 = getResult(invitedList: invited_list1, dislikeList: dislike_list1)
print(intT1)
// Test #2
let invited_list2 = 5
let dislike_list2 = ["1-2,3", "3-4,5", "2-3"]
let intT2 = getResult(invitedList: invited_list2, dislikeList: dislike_list2)
print(intT2)

Тест #4 (тут все тест кейсы сильно сопротивлялись :) )

Задание:

Существует массив из N количества 3 видов букв - ["x", "x", "y", "z"]

Если буквы заменить на целые числа, то можно ли получить общую сумму в X число

Пример:

// Test #1

let sub_array = ["x", "x", "x", "y", "y"]

let k = 12

let result = getResult(sub_array, k)

print(result)

// = True

// Можно заменить x на 2, y на 3, тогда получится

//[2, 2, 2, 3, 3]

func getResult(_ subArray: [String], _ k: Int) -> Bool {
    if subArray.count == 1 {
        return true
    }
    if subArray.count > k {
        return false
    }
    if subArray.count == k {
        return true
    }
        
    var tempSetOfTypeLetters: Set<String> = []
    var dictOfCountOfLetters: [String: Int] = [:]
    subArray.forEach { str in
        tempSetOfTypeLetters.insert(str)
        if !dictOfCountOfLetters.keys.contains(str) {
            dictOfCountOfLetters[str] = 1
        } else {
            dictOfCountOfLetters[str]! += 1
        }
    }
    var totalCount: Int = 0
    var tempArray: [Int] = []
    for indexA in dictOfCountOfLetters {
        tempArray.append(indexA.value)
        totalCount = totalCount + indexA.value
    }
    tempArray = tempArray.sorted{$0>$1}
    print("Well done - tempArray is - ", tempArray)

    var tempCounter: Int = 0
    while tempCounter < k {
        tempCounter += 1
        var tempArrOfInt:[Int] = []
        
        for indexE in 0..<tempArray.count-1 {
            tempArrOfInt.append(tempArray[indexE] * tempCounter)
            print("tempArrOfInt.append - ", tempArrOfInt)
            if indexE >= 0 {
                print("<<<---->>>")
                print("indexE is - ", indexE)
                for indexY in 1..<k {
                    var temp:[Int] = []
                    temp.append(tempArray[indexE+1] * (tempCounter + indexY))
                    print("temp appended - ", temp )
                    if tempArray.count == 3 {
                        print("tempArray.count == 3 - ", tempArray.count)
                        for indexK in (indexY+1)..<k {
                            temp.append(tempArray[2] * (tempCounter + indexK))
                            print("temp appended - ", temp)
                            var total = tempArrOfInt.first
                            print("total 2.1 - ", total)
                            temp.forEach{ total = total! + $0 }
                            print("total 2.2 is - ", total as Any)
                            if (k - total!) == 0 {
                                return true
                            }
                        }
                    }

                    print("temp - ", temp)
                    var total = tempArrOfInt.first
                    print("total 1.1 - ", total)
                    temp.forEach{ total = total! + $0 }
                    print("total is 1.2 - ", total as Any)
                    if (k - total!) == 0 {
                        return true
                    }
                }
            }
        }
    }
    return false

}

// Test #1
let sub_array = ["x", "x", "x", "y", "y"]
let k = 12
let result = getResult(sub_array, k)
print(result)
// = True
// Можно заменить x на 2, y на 3, тогда получится
//[2, 2, 2, 3, 3]

// Test #2
let sub_array2 = ["x", "x", "y", "y"]
let k2 = 20
let result2 = getResult(sub_array2, k2)
print(result2)

// Test #3
let sub_array4 = ["x", "x"]
let k4 = 2
let result4 = getResult(sub_array4, k4)
print(result4)

// Test #4
let sub_array5 = ["x", "x", "y"]
let k5 = 4
let result5 = getResult(sub_array5, k5)
print(result5)

// Test #5
let sub_array6 = ["x", "x", "y", "z"]
let k6 = 24
let result6 = getResult(sub_array6, k6)
print(result6)

// Test #6
let sub_array7 = ["x", "x", "y", "z"]
let k7 = 34
let result7 = getResult(sub_array7, k7)
print(result7)

Тест #5 (Тут я упустил, что убирать можно не только первые символы, но и в середине, из-за чего быстрее положенного все тест кейсы не прошел. На том и остановился...)

Задание:

 Дана строка s. Вы можете удалить из нее не более k символов. Определите максимально возможное количество совпадений символов с stringGoal, то есть совпадением считается s[i] = stringGoal[i]. Например, строка "agddb" совпадает с "gdda" только одним символом. Если убрать первый символ, тогда будет уже 3 совпадения - "gddb" "gdda".

Ввод:

 s - строка с символами, 0<length(s)<20

 k - максимальное количество удалений, 0<k<10

 stringGoal - строка для сравнения

 Вывод:

Integer - максимальное количество совпадений s[i] = stringGoal[i]

 Пример:

 s = "agdd"

 k = 1

 stringGoal = "gdd"

 getResult(s, k, stringGoal) = 3

extension String {
    var length: Int {
        return count
    }
    subscript (i: Int) -> String {
        return self[i ..< i + 1]
    }
    func substring(fromIndex: Int) -> String {
        return self[min(fromIndex, length) ..< length]
    }
    func substring(toIndex: Int) -> String {
        return self[0 ..< max(0, toIndex)]
    }
    subscript (r: Range<Int>) -> String {
        let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                            upper: min(length, max(0, r.upperBound))))
        let start = index(startIndex, offsetBy: range.lowerBound)
        let end = index(start, offsetBy: range.upperBound - range.lowerBound)
        return String(self[start ..< end])
    }
}

func getResult(_ s: String, _ k: Int, _ stringGoal: String) -> Int {
    var maxCounterOfMatched: Int = 0
//  Первая попытка
//  проходит Тестов 6 из 10
    var flagFromPrefixToBody: Bool = false
    var whileCounter: Int = 0
    while whileCounter <= k {
        var tempMainString: String = ""

        if flagFromPrefixToBody == false {
            tempMainString = s.substring(fromIndex: whileCounter)
            print("do it 1")
        } else {
            var tempString = s
            if tempString.count > (whileCounter+1) {
                let indexToDelete = tempString.firstIndex(of: Character(tempString[whileCounter+1]))!
                tempString.remove(at: indexToDelete)
                tempMainString = tempString
            }
            print("do it 2")
        }
        print("tempMainString is - ", tempMainString)

        var counterOfMatched: Int = 0
        for indexA in 0..<tempMainString.count {
            if tempMainString[indexA] == stringGoal[indexA] {
                counterOfMatched += 1
            }
        }
        print("compare results")
        if maxCounterOfMatched < counterOfMatched {
            maxCounterOfMatched = counterOfMatched
        }
        if whileCounter == k && flagFromPrefixToBody == false {
            flagFromPrefixToBody = true
            whileCounter = 0
        }
        whileCounter += 1
    }
    print("Part #1 finished, maxCounterOfMatched - ", maxCounterOfMatched)
  
//  Вторая попытка
    print("<<---->>")
    print("Part# 2")
    
    for indexA in 0..<s.count {
        print("do it 1")
        for indexB in 0...k {
            print("do it 3")
            var tempString = s
            let startIndex = tempString.index(tempString.startIndex, offsetBy: indexA)
            //print("startIndex - ", startIndex)
            guard let endIndex = tempString.index(tempString.startIndex, offsetBy: (indexA + indexB)) as? String.Index else {
                break
            }
            //print("endIndex - ", endIndex)
            if startIndex >= endIndex {
                break
            }
            tempString.removeSubrange(startIndex...endIndex)
            print("tempString - ", tempString)
            print("do it 4")
            var counterOfMatched: Int = 0
            for indexC in 0..<tempString.count {
                if tempString[indexC] == stringGoal[indexC] {
                    counterOfMatched += 1
                }
            }
            if maxCounterOfMatched < counterOfMatched {
                maxCounterOfMatched = counterOfMatched
            }
        }
    }
    /*
     вторая часть проходит Тестов 6 из 10
     */

    return maxCounterOfMatched
}
// 1,2,3,6,7,8
// need - 4,5,9,10
// need - 3,7,8,10
// (stringGoal.count - 1,2,3,6,7,8)

// need - 2,6,7,8

let s = "agdd"
let k = 1
let stringGoal = "gdd"
let result = getResult(s, k, stringGoal) //= 3
print(result)

let s2 = "ababcde"
let k2 = 3
let stringGoal2 = "abcde"
let result2 = getResult(s2, k2, stringGoal2) //= 5
print(result2)


let s3 = "aqwerty"
let k3 = 3
let stringGoal3 = "qwerty"
let result3 = getResult(s3, k3, stringGoal3)
print(result3)

// Test that fails
let s4 = "aqawerty"
let k4 = 3
let stringGoal4 = "qwerty"
let result4 = getResult(s4, k4, stringGoal4)
print(result4)

Автор:
Maxnxi

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js