Давши рядок з мільйона чисел, поверніть усі повторювані трицифрові числа


137

Я провів співбесіду з компанією з хедж-фондів у Нью-Йорку кілька місяців тому, і, на жаль, я не отримав пропозицію про стажування як інженер з даних / програмного забезпечення. (Вони також попросили рішення бути в Python.)

Я досить сильно накрутив першу проблему інтерв'ю ...

Запитання: Враховуючи рядок з мільйона чисел (наприклад, Pi), напишіть функцію / програму, яка повертає всі повторювані трицифрові числа та кількість повторень більше 1

Наприклад: якщо рядок був: 123412345123456тоді функція / програма повертається:

123 - 3 times
234 - 3 times
345 - 2 times

Вони не дали мені рішення після того, як я не вдався до співбесіди, але вони сказали мені, що часова складність рішення була постійною 1000, оскільки всі можливі результати між:

000 -> 999

Тепер, коли я думаю про це, я не думаю, що можна придумати алгоритм постійного часу. Є це?


68
Якщо вони вважають, що рішення є постійною 1000, то це змушує мене думати, що вони побудували б усі тризначні числа, а потім шукали їх регулярним виразом. Люди дуже часто думають, що операції, які вони насправді не писали / не бачили, є "безкоштовними". Я майже впевнений, що це буде лінійно до довжини струни.
mypetlion

54
Мимоволі, якщо розмір вводу постійний, кожен алгоритм є постійним часом ;-)
Paŭlo Ebermann

34
константа 1000 що ? (доповнення? слони?)
ilkkachu

31
Ну, якщо довжина рядка є постійною (1 М), а довжина підрядки / числа - постійною (3), то технічно кожне рішення є постійним часом…
Кевін

8
They did not give me the solution after I failed the interview, but they did tell me that the time complexity for the solution was constant of 1000 since all the possible outcomes are between: 000 --> 999 Ймовірно, це було фактичним випробуванням. Щоб побачити, чи можете ви довести їм, чому це неможливо, і показати їм правильну мінімальну часову складність.
Джеймс

Відповіді:


168

Ви злегка зійшли, ви, мабуть , не хочете працювати в хедж-фонді, де учасники не розуміють основних алгоритмів :-)

Там немає НЕ способу обробити довільно розмір структуру даних в O(1)разі, так як в цьому випадку, ви повинні відвідати кожен елемент , по крайней мере один раз. Краще ви можете сподіватися на те , O(n)в цьому випадку, коли nце довжина рядка.

Хоча, як і осторонь, номінальний O(n)алгоритм буде встановлений O(1)для фіксованого розміру вводу, тому технічно вони, можливо, тут були правильними. Однак, як правило, люди не використовують аналіз складності.

Мені здається, ви могли вразити їх різними способами.

По- перше, повідомивши їм , що це НЕ можливо зробити це O(1), якщо ви не використовувати «підозрюваний» міркування , наведені вище.

По-друге, показавши свої елітні навички, надавши пітонічний код, наприклад:

inpStr = '123412345123456'

# O(1) array creation.
freq = [0] * 1000

# O(n) string processing.
for val in [int(inpStr[pos:pos+3]) for pos in range(len(inpStr) - 2)]:
    freq[val] += 1

# O(1) output of relevant array values.
print ([(num, freq[num]) for num in range(1000) if freq[num] > 1])

Цей результат:

[(123, 3), (234, 3), (345, 2)]

хоча ви, звичайно, можете змінити вихідний формат на все, що завгодно.

І, нарешті, кажучи їм, що з рішенням майже не існує жодної проблеми O(n), оскільки наведений вище код дає результати на мільйонний рядок трохи менше півсекунди. Схоже, він масштабується досить лінійно, оскільки 10 000 000 символів займає 3,5 секунди, а 100 000 000 символів - 36 секунд.

І якщо вони потребують кращого за це, існують способи паралелізації такого роду матеріалів, які можуть значно пришвидшити його.

Звичайно, не в межах одного інтерпретатора Python, завдяки GIL, але ви можете розділити рядок на щось подібне (перекриття, вказане символом, vvнеобхідне для правильної обробки прикордонних областей):

    vv
123412  vv
    123451
        5123456

Ви можете обробляти їх, щоб розділити працівників і згодом об'єднати результати.

Розщеплення вхідних даних та об'єднання результатів, ймовірно, можуть заповнити будь-яку економію невеликими рядками (і, можливо, навіть мільйонними рядками), але для набагато більших наборів даних це може змінити значення. Звичайно, тут застосовується моя звичайна мантра "міри, не здогадуйся" .


Ця мантра застосовується і до інших можливостей, таких як взагалі обхід Python та використання іншої мови, яка може бути швидшою.

Наприклад, наступний код C, який працює на тому ж апаратному забезпеченні, що і попередній код Python, обробляє сто мільйонів цифр за 0,6 секунди, приблизно стільки ж часу, скільки код Python обробив один мільйон. Іншими словами, набагато швидше:

#include <stdio.h>
#include <string.h>

int main(void) {
    static char inpStr[100000000+1];
    static int freq[1000];

    // Set up test data.

    memset(inpStr, '1', sizeof(inpStr));
    inpStr[sizeof(inpStr)-1] = '\0';

    // Need at least three digits to do anything useful.

    if (strlen(inpStr) <= 2) return 0;

    // Get initial feed from first two digits, process others.

    int val = (inpStr[0] - '0') * 10 + inpStr[1] - '0';
    char *inpPtr = &(inpStr[2]);
    while (*inpPtr != '\0') {
        // Remove hundreds, add next digit as units, adjust table.

        val = (val % 100) * 10 + *inpPtr++ - '0';
        freq[val]++;
    }

    // Output (relevant part of) table.

    for (int i = 0; i < 1000; ++i)
        if (freq[i] > 1)
            printf("%3d -> %d\n", i, freq[i]);

    return 0;
}

19
Цей "фіксований розмір введення" насправді звучить як поганий жарт або інтерв'юер, або інтерв'ю не отримали. Кожен алгоритм стає O(1)є nфіксованим або обмеженим.
Ерік Думініл

5
Якщо їм це потрібно краще, можливо, вони не повинні використовувати Python, принаймні для конкретного алгоритму.
Себастьян Редл

3
@ezzzCash Тому що при спробах паралельного підходу може виникнути перекриття в точках, де "розривається" рядок. Оскільки ви шукаєте трицифрові групи, -2 дозволяє перевірити обидві паралельні групування, щоб не пропустити потенційно дійсну відповідність.
code_dredd

5
@ezzzCash Це не брак знань про паралельне програмування. Розглянемо рядок довжини N. Якщо ви розділите його на дві частини в положенні N/2, вам все одно потрібно врахувати той факт, що ви могли пропустити дійсну тризначну відповідність на "кордоні", в кінці string1та на початку string2. Таким чином, вам потрібно перевірити відповідність між string1[N/2-2]та string2[2](використовуючи нульовий індекс) тощо. Це ідея.
code_dredd

1
З довшими цифрами-послідовностями можна отримати щось від оптимізації перетворення в ціле число з розсувним вікном, яке дозволяє скидати найвищу цифру та додати нову цифру. (Накладні витрати Python, ймовірно, вбивають це, тому воно буде застосовуватися лише до C або інших низькорівневих реалізацій). val -= 100 * (d[i]-'0');скинути провідну цифру. val = 10*val + d[i+2]-'0'щоб накопичити нову найменш значущу цифру (нормальний рядок-> цілий розбір). val % 100Можливо, це не жахливо, але тільки якщо 100це константа часу компіляції, тому він не використовує реальний розподіл HW.
Пітер Кордес

78

Постійний час неможливий. Усі 1 мільйон цифр потрібно переглянути хоча б один раз, так що це часова складність O (n), де n = 1 мільйон у цьому випадку.

Для простого рішення O (n) створіть масив розміром 1000, який представляє кількість входів кожного можливого тризначного числа. Заздалегідь просуньте 1 цифру, перший індекс == 0, останній індекс == 999997 та інкрементний масив [3-значний номер], щоб створити гістограму (підрахунок подій для кожного можливого тризначного числа). Потім виведіть вміст масиву з підрахунками> 1.


26
@ezzzCash - так, словник працював би, але він не потрібен. Усі можливі "клавіші" відомі заздалегідь, обмежуючись діапазоном від 0 до 999. Різниця накладних витрат - це час, який потрібен для доступу на основі клавіш, використовуючи 3 символьні рядки як ключі, порівняно з часом, необхідним для перетворення 3 розрядний рядок до індексу, а потім за допомогою індексу для доступу до масиву.
rcgldr

4
Якщо ви хочете числові хитрощі, ви також можете прийняти BCD і зберегти три цифри в 12 біт. І розшифруйте ASCII цифри, маскуючи низькі 4 біти. Але цей x-'0'шаблон не дійсний у Python, це C-ism (де символи є цілими числами).
Yann Vernier

5
@LorenPechtel: Пошуки словників у Python дійсно швидкі. Здійснено доступ до масиву ще швидше, тому якби ми мали справу з цілими числами з самого початку, ви мали би рацію. Однак у цьому випадку у нас є тривимірні рядки, які ми спочатку повинні перетворити на цілі числа, якщо ми хочемо використовувати їх з масивами. Виявляється, всупереч тому, що можна спочатку очікувати, пошук словника насправді швидший, ніж цілочисельне перетворення + доступ до масиву. Рішення масиву насправді на 50% повільніше в цьому випадку.
Алексі Торхамо

2
Я думаю, можна стверджувати, що якщо число вводу завжди має рівно 1 мільйон цифр, то в цьому алгоритмі є O (1) з постійним коефіцієнтом 1 мільйон.
tobias_k

2
@AleksiTorhamo - Якщо метою є порівняння відносних швидкостей реалізації алгоритму, я віддаю перевагу традиційній мові, як C або C ++, оскільки Python значно повільніший і, здається, має накладні витрати, унікальні для Python порівняно з іншими мовами.
rcgldr

14

Мільйон малий за відповідь, яку я даю нижче. Очікуючи лише на те, що вам доведеться запускати рішення в інтерв'ю без паузи, тоді наступне працює менше ніж за дві секунди і дає необхідний результат:

from collections import Counter

def triple_counter(s):
    c = Counter(s[n-3: n] for n in range(3, len(s)))
    for tri, n in c.most_common():
        if n > 1:
            print('%s - %i times.' % (tri, n))
        else:
            break

if __name__ == '__main__':
    import random

    s = ''.join(random.choice('0123456789') for _ in range(1_000_000))
    triple_counter(s)

Сподіваємось, інтерв'юер шукатиме використання стандартних колекцій бібліотек.

Версія для паралельного виконання

Про це я написав повідомлення в блозі з додатковими поясненнями.


Це добре працює і, здається, є найшвидшим рішенням, яке не викликає нуд.
Ерік Думініл

3
@EricDuminil, я не думаю, що ви повинні турбуватися про те, щоб мати тут найчастіші терміни, коли більшість наданих рішень не затримають вас сильно. Набагато краще показати, що ти добре розумієш стандартну бібліотеку Python і можеш написати простір коду в ситуації інтерв'ю, я думаю. (Якщо тільки інтерв'юер не наголосив на критичній часовій критиці, після чого вам слід запитати про фактичні терміни, перш ніж оцінювати, що буде далі).
Paddy3118

1
Ми погоджуємося на 100%. Хоча я не впевнений, що відповідь взагалі є актуальною, якщо інтерв'юер дійсно вважає, що це можливо зробити O(1).
Ерік Думініл

1
Якщо інтерв'юер підкреслив, що це критично важливий час, то, після профілювання, щоб підтвердити це межа, можливо, настав час написати модуль С для вирішення цього вузького місця. У мене є сценарій, який побачив поліпшення 84x над кодом python після того, як ми перейшли на використання модуля змінного струму.
TemporalWolf

Привіт @TemporalWolf, я прочитав те, що ви сказали, тоді подумав, що іншим, швидшим і масштабованим рішенням може бути зміна його на паралельний алгоритм, щоб він міг працювати у багатьох процесах на обчислювальній фермі / хмарі. Ви повинні розділити рядок на n секцій; перекриваючи останні 3 символи кожного розділу своїм наступним розділом. Кожен розділ може бути сканований на трійку незалежно, трійки підсумовані, а три знаки трійки в кінці всіх, окрім останнього розділу, віднімаються так, як це було б подраховано. У мене є код, і, ймовірно, перетворять його на допис у блозі ...
Paddy3118

13

Простим рішенням O (n) було б підрахувати кожне трицифрове число:

for nr in range(1000):
    cnt = text.count('%03d' % nr)
    if cnt > 1:
        print '%03d is found %d times' % (nr, cnt)

Це дозволить шукати всі 1 мільйон цифр 1000 разів.

Переміщення цифр лише один раз:

counts = [0] * 1000
for idx in range(len(text)-2):
    counts[int(text[idx:idx+3])] += 1

for nr, cnt in enumerate(counts):
    if cnt > 1:
        print '%03d is found %d times' % (nr, cnt)

Час показує, що повторення лише одного разу над індексом удвічі швидше, ніж використання count.


37
Чи знижка на чорну п'ятницю text.count()?
Ерік Думініл

3
@EricDuminil У вас є хороший момент, але, оскільки text.countце робиться на високошвидкісній компільованій мові (наприклад, C), на відміну від повільного циклу інтерпретації python, так, є знижка.
John1024

Дуже рахувати кожне число окремо неефективно, але це постійний час, таким чином, все одно O (n).
Лорен Печтел

11
Варіант, який ви запропонували, що використовує, countє невірним, оскільки він не враховує шаблони, що перекриваються. Зауважте, що '111'.count('11') == 1коли б ми очікували, що це буде 2.
Сірео

2
Крім того , ваше «просте O(n)рішення» насправді O(10**d * n)з dчислом шуканих чисел і nзагальною довжиною рядка. Другий - це O(n)час і O(10**d + n)простір.
Ерік Думініл

10

Ось реалізація NumPy алгоритму "консенсусу" O (n): пройдіться через усі трійки та бін, як ви йдете. Бінінг проводиться, якщо зустріти слово "385", додавши його до біна [3, 8, 5], що є операцією O (1). Бункери розташовані в 10x10x10кубі. Оскільки бінінг повністю векторизований, в коді немає циклу.

def setup_data(n):
    import random
    digits = "0123456789"
    return dict(text = ''.join(random.choice(digits) for i in range(n)))

def f_np(text):
    # Get the data into NumPy
    import numpy as np
    a = np.frombuffer(bytes(text, 'utf8'), dtype=np.uint8) - ord('0')
    # Rolling triplets
    a3 = np.lib.stride_tricks.as_strided(a, (3, a.size-2), 2*a.strides)

    bins = np.zeros((10, 10, 10), dtype=int)
    # Next line performs O(n) binning
    np.add.at(bins, tuple(a3), 1)
    # Filtering is left as an exercise
    return bins.ravel()

def f_py(text):
    counts = [0] * 1000
    for idx in range(len(text)-2):
        counts[int(text[idx:idx+3])] += 1
    return counts

import numpy as np
import types
from timeit import timeit
for n in (10, 1000, 1000000):
    data = setup_data(n)
    ref = f_np(**data)
    print(f'n = {n}')
    for name, func in list(globals().items()):
        if not name.startswith('f_') or not isinstance(func, types.FunctionType):
            continue
        try:
            assert np.all(ref == func(**data))
            print("{:16s}{:16.8f} ms".format(name[2:], timeit(
                'f(**data)', globals={'f':func, 'data':data}, number=10)*100))
        except:
            print("{:16s} apparently crashed".format(name[2:]))

Не дивно, що NumPy трохи швидше, ніж чисте рішення Python @ Daniel на великих наборах даних. Вибірка зразка:

# n = 10
# np                    0.03481400 ms
# py                    0.00669330 ms
# n = 1000
# np                    0.11215360 ms
# py                    0.34836530 ms
# n = 1000000
# np                   82.46765980 ms
# py                  360.51235450 ms

Можливо, значно швидше вирівняти цифровий рядок замість того, щоб вкладати вставки, якщо NumPy не реалізує його як 3D-матрицю з ефективною індексацією. Проти якої версії @ Daniel's ви колись проти; той, який виконує пошук рядків для кожного цілого числа, або той, що має гістограму?
Пітер Кордес

2
@PeterCordes Я сумніваюся в цьому. ndarrays, основний тип numpy, все стосується ефективного зберігання, маніпулювання та індексації багатовимірних масивів чисел. Іноді ви можете збрити кілька відсотків шляхом вирівнювання, але в цьому випадку виконання 100 x [0] + 10 x [1] + x [2] вручну не принесе вам багато чого. Я використовував те, що @Daniel сказав швидше, ви можете перевірити код еталону самостійно.
Пол Панцер

Я насправді не знаю NumPy (або взагалі Python; я в основному роблю налаштування продуктивності C і збірки на x86), але я думаю, у вас є один 3D-масив, правда? Я думав з вашого англійського тексту (який, мабуть, я навіть не читав уважно), що ви фактично вклали об’єкти Python і індексували їх окремо. Але це не так, тому nvm мій перший коментар.
Пітер Кордес

Я думаю, що використовувана вами чиста версія Python - це майже та сама реалізація гістограми, що навіть більш високі голосові відповіді використовуються, але якщо різні способи її написання в Python сильно впливають на швидкість.
Пітер Кордес

3

Я вирішував би проблему так:

def find_numbers(str_num):
    final_dict = {}
    buffer = {}
    for idx in range(len(str_num) - 3):
        num = int(str_num[idx:idx + 3])
        if num not in buffer:
            buffer[num] = 0
        buffer[num] += 1
        if buffer[num] > 1:
            final_dict[num] = buffer[num]
    return final_dict

Застосований у вашому прикладі рядка, це дає:

>>> find_numbers("123412345123456")
{345: 2, 234: 3, 123: 3}

Це рішення працює в O (n), оскільки n є довжиною наданого рядка, і, я думаю, найкраще, що ви можете отримати.


Ви можете просто використовувати Counter. Вам не потрібен final_dictі не потрібно оновлювати його під час кожної ітерації.
Ерік Думініл

2

Як я розумію, ви не можете мати рішення постійно. Це займе щонайменше один пропуск понад мільйонне число (якщо вважати його рядком). Ви можете мати тризначну ітерацію прокатки над цифрами мільйонної довжини і збільшити значення хеш-ключа на 1, якщо він вже існує, або створити новий хеш-ключ (ініціалізований значенням 1), якщо він ще не існує в словник.

Код буде виглядати приблизно так:

def calc_repeating_digits(number):

    hash = {}

    for i in range(len(str(number))-2):

        current_three_digits = number[i:i+3]
        if current_three_digits in hash.keys():
            hash[current_three_digits] += 1

        else:
            hash[current_three_digits] = 1

    return hash

Ви можете відфільтрувати до клавіш, значення яких перевищує 1.


2

Як було сказано в іншій відповіді, ви не можете виконувати цей алгоритм постійно, тому що ви повинні переглянути принаймні n цифр. Лінійний час - це найшвидший показник.

Однак алгоритм можна виконати в просторі O (1) . Вам потрібно лише зберігати підрахунки кожного трицифрового числа, тому вам потрібен масив з 1000 записів. Потім ви можете передати номер у.

Я здогадуюсь, що або інтерв'юер пропустив помилку, коли вони дали вам рішення, або ви неправильно почули "постійний час", коли вони сказали "постійний простір".


Як зазначають інші, підхід гістограми - це O(10**d)додатковий простір, де d- кількість десяткових цифр, яку ви шукаєте.
Пітер Кордес

1
Підхід до словника буде O (min (10 ^ d, n)) для n цифр. Наприклад, якщо у вас n = 10 ^ 9 цифр і ви хочете знайти рідкісні 15-значні послідовності, які трапляються не один раз.
gnasher729

1

Ось моя відповідь:

from timeit import timeit
from collections import Counter
import types
import random

def setup_data(n):
    digits = "0123456789"
    return dict(text = ''.join(random.choice(digits) for i in range(n)))


def f_counter(text):
    c = Counter()
    for i in range(len(text)-2):
        ss = text[i:i+3]
        c.update([ss])
    return (i for i in c.items() if i[1] > 1)

def f_dict(text):
    d = {}
    for i in range(len(text)-2):
        ss = text[i:i+3]
        if ss not in d:
            d[ss] = 0
        d[ss] += 1
    return ((i, d[i]) for i in d if d[i] > 1)

def f_array(text):
    a = [[[0 for _ in range(10)] for _ in range(10)] for _ in range(10)]
    for n in range(len(text)-2):
        i, j, k = (int(ss) for ss in text[n:n+3])
        a[i][j][k] += 1
    for i, b in enumerate(a):
        for j, c in enumerate(b):
            for k, d in enumerate(c):
                if d > 1: yield (f'{i}{j}{k}', d)


for n in (1E1, 1E3, 1E6):
    n = int(n)
    data = setup_data(n)
    print(f'n = {n}')
    results = {}
    for name, func in list(globals().items()):
        if not name.startswith('f_') or not isinstance(func, types.FunctionType):
            continue
        print("{:16s}{:16.8f} ms".format(name[2:], timeit(
            'results[name] = f(**data)', globals={'f':func, 'data':data, 'results':results, 'name':name}, number=10)*100))
    for r in results:
        print('{:10}: {}'.format(r, sorted(list(results[r]))[:5]))

Метод пошуку масиву дуже швидкий (навіть швидший, ніж метод нумеру @ paul-panzer!). Звичайно, він обманює, оскільки не закінчується технічно після його завершення, тому що повертає генератор. Також не потрібно перевіряти кожну ітерацію, якщо значення вже існує, що, ймовірно, дуже допоможе.

n = 10
counter               0.10595780 ms
dict                  0.01070654 ms
array                 0.00135370 ms
f_counter : []
f_dict    : []
f_array   : []
n = 1000
counter               2.89462101 ms
dict                  0.40434612 ms
array                 0.00073838 ms
f_counter : [('008', 2), ('009', 3), ('010', 2), ('016', 2), ('017', 2)]
f_dict    : [('008', 2), ('009', 3), ('010', 2), ('016', 2), ('017', 2)]
f_array   : [('008', 2), ('009', 3), ('010', 2), ('016', 2), ('017', 2)]
n = 1000000
counter            2849.00500992 ms
dict                438.44007806 ms
array                 0.00135370 ms
f_counter : [('000', 1058), ('001', 943), ('002', 1030), ('003', 982), ('004', 1042)]
f_dict    : [('000', 1058), ('001', 943), ('002', 1030), ('003', 982), ('004', 1042)]
f_array   : [('000', 1058), ('001', 943), ('002', 1030), ('003', 982), ('004', 1042)]

1
То що ти порівнюєш саме? Чи не повинні ви повертати списки замість невикористаних генераторів?
Ерік Думініл

Countersне використовуються таким чином. Використовуючи правильно, вони стають найшвидшим варіантом із вашим прикладом. Якщо ви використовуєте timeitсписок, вкладений у генератор, ваш метод стає повільнішим за Counterабо dict. Дивіться тут .
Ерік Думініл

Нарешті, ваш f_arrayможе бути швидшим, якщо спершу перетворити кожну таблицю в int: ints = [int(c) for c in text]і потім використовувати i, j, k = ints[n:n+3].
Ерік Думініл


1

Ось моє рішення:

from collections import defaultdict
string = "103264685134845354863"
d = defaultdict(int)
for elt in range(len(string)-2):
    d[string[elt:elt+3]] += 1
d = {key: d[key] for key in d.keys() if d[key] > 1}

Проявивши трохи творчості для циклу (і додаткового списку пошуку, наприклад, з True / False / None), ви повинні мати можливість позбутися останнього рядка, оскільки ви хочете створити лише ключі в протоколі, які ми відвідували один раз до цього моменту . Сподіваюся, це допомагає :)


Дивіться відповідь pho7 . І коментарі. Спробуйте розібратися, чому він не отримує багато голосів.
сіра борода

0

-Повідомлення з точки зору C. -Ви можете мати int 3-d масиви результатів [10] [10] [10]; -Перехід від 0-го місця до n-четвертого місця, де n - розмір рядкового масиву. -На кожній локації перевіряйте поточну, наступну та наступну наступну. -Повищення cntr як resutls [current] [next] [next's next] ++; -Надрукуйте значення

results[1][2][3]
results[2][3][4]
results[3][4][5]
results[4][5][6]
results[5][6][7]
results[6][7][8]
results[7][8][9]

-Це час O (n), порівнянь не бере. -Ви можете запустити паралельні речі тут, розділивши масив і обчисливши відповідність навколо розділів.


-1
inputStr = '123456123138276237284287434628736482376487234682734682736487263482736487236482634'

count = {}
for i in range(len(inputStr) - 2):
    subNum = int(inputStr[i:i+3])
    if subNum not in count:
        count[subNum] = 1
    else:
        count[subNum] += 1

print count

Дякую за вашу відповідь, але він занадто схожий на алгоритм, як це було дано @abhishek arora 5-6 днів тому. Також в оригінальному запитанні було не запитання алгоритму, а інше питання (на яке вже відповіли кілька разів)
its.david
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.