Визначте рядок із його підрядків


20

Вступ

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

Завдання

Вашими входами буде не порожній рядок Sнад алфавітом abcта його довжиною, а вихідний - S. Без обмежень це, звичайно, буде тривіальним завданням; Привід полягає в тому, що вам не дозволяють отримати доступ Sбезпосередньо. Єдине, що вам дозволено робити - Sце викликати функцію num_occur(T, S), де Tє якась інша рядок, і num_occurрахувати кількість входів Tу S. Події, що перекриваються, зараховуються як окремі, тому num_occur(T, S)дійсно повертає кількість індексів iтаких, що

S[i, i+1, …, i+length(T)-1] == T

Наприклад, num_occur("aba", "cababaababb")повернеться 3. Зверніть увагу також на те num_occur(S, S), що повернеться 1. Результат num_occur("", S)не визначений, і ви не повинні викликати функцію на порожній рядку.

Коротше кажучи, ви повинні написати функцію або програму, яка приймає Sі length(S)вводить, викликає num_occurкілька коротших рядків і Sдеяку кількість разів, реконструює Sцю інформацію та повертає її.

Правила та оцінка

Ваша мета - написати програму, яка здійснює якомога менше дзвінків num_occur. У цьому сховищі ви знайдете файл з назвою abc_strings.txt. Файл містить 100 рядків, кожен у своєму рядку, між довжинами 50 і 99. Ваш бал - це загальна кількість викликів num_occurна цих входах , нижчий бал - кращий. Переважно, ваше рішення буде відслідковувати це число під час його запуску та друкувати його після завершення. Рядки створюються шляхом вибору рівномірно випадкових літер abc; вам дозволяється оптимізувати цей метод генерації рядків, але не самі рядки.

Не існує обмеження часу, за винятком того, що ви повинні запустити своє рішення на тестових випадках, перш ніж подавати його. Ваше рішення повинно працювати для будь-яких дійсних даних S, а не лише для тестових випадків.

Вам рекомендується також поділитися своєю реалізацією num_occur, якщо ви не використовуєте чужу. Для того, щоб куля прокотилася, ось реалізація в Python:

def num_occur(needle, haystack):
    num = 0
    for i in range(len(haystack) - len(needle) + 1):
        if haystack[i : i + len(needle)] == needle:
            num += 1
    return num

Чи повинні наші алгоритми працювати для всіх можливих рядків Sабо лише для тестових випадків?
Loovjo

@Loovjo Добре запитання. Вони теоретично повинні працювати для всіх непустих рядків. Я відредагую виклик.
Згарб

all non-empty stringsбудь-якої довжини?
edc65

@ edc65 Теоретично так. Ви можете ігнорувати обмежені адреси пам'яті та інші практичні обмеження.
Згарб

Можна додати алгоритм VW, щоб успішно пройти тест оцінювання: спочатку перевірте на наявність відомих рядків abc_strings.txt
Еммануель

Відповіді:


6

Javascript, 14325 14311 дзвінків

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

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

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

var test = [
  'ccccbcbbbbacbaaababbccaacbccaaaaccbccaaaaaabcbbbab',
  // etc.
];
var call = 0;

function guess(S, len) {
  var sym = {};
  recurse(S, len, "", sym);
  return sym.result;
}

function recurse(S, len, s, sym) {
  var dictionary = [];

  if(s == '' || (isCandidate(s, sym) && (sym[s] = numOccur(S, s)))) {
    if(s.length == len) {
      sym.result = s;
    }
    else if(sym['a'] && count(s, 'a') == sym['a'] - (len - s.length)) {
      dictionary = [ Array(len - s.length + 1).join('a') ];
    }
    else {
      dictionary = [ "a", "b", "c" ];
    }
    dictionary.some(function(e) {
      return recurse(S, len, s + e, sym) || recurse(S, len, e + s, sym);
    });
    return true;
  }
  return false;
}

function isCandidate(s, sym) {
  return sym[s] === undefined && Object.keys(sym).every(function(k) {
    return count(s, k) <= sym[k];
  });
}

function count(s0, s1) {
  return (s0.match(new RegExp(s1, 'g')) || []).length;
}

function numOccur(S, s) {
  call++;
  return count(S, s);
}

test.forEach(function(S) {
  if(guess(S, S.length) != S) {
    console.log("Failed for: '" + S + "'");
  }
});
console.log(call + " calls");

Повний виконаний фрагмент нижче.


"Коротше кажучи, ви повинні написати функцію чи програму, яка [...], реконструює S із цієї інформації та повертає її ."
КарлКастор

@KarlKastor - На жаль. Ти маєш рацію. Це виправлено. Спасибі!
Арнольд

4

Python, 15205 дзвінків

def findS(S, len_s, alphabeth = "abc"):
    if len_s == 0:
        return ""
    current = ""
    add_at_start = True
    while len(current) < len_s:
        worked = False 
        for letter in alphabeth:
            if add_at_start:
                current_new = current + letter
            else:
                current_new = letter + current
            if num_occur(current_new, S) > 0:
                current = current_new
                worked = True
                break
        if not worked:
            add_at_start = False
    return current 

Це подання, швидше за все, неоптимальне, оскільки воно використовує лише num_occurдля перевірки, чи є рядок підрядком S, і ніколи не використовує її для фактичного підрахунку кількості підрядків.

Алгоритм працює за допомогою збереження рядка, currentякий будується таким, щоб він був рівним Sв кінці. Ось усі кроки в алгоритмі:

  1. Ставимо currentрівним''

  2. Пройдіть кожну букву в алфавіті та виконайте наступне:

    2.1. Створіть новий рядок current_newі встановіть, що він дорівнює currentлітери.

    2.2. Перевірте, чи current_newвключено в Sнього, натискаючи num_occurна нього, і перевірте, чи результат більший за один.

    2.3. Якщо current_newвключена в S, набір currentдо current_newі повернутися до кроку 2. В іншому випадку, ми переходимо до наступного листа.

  3. Якщо довжина currentдорівнює довжині, Sми можемо сказати, що ми зробили. В іншому випадку ми повернемось до кроку 2, але змінимо крок 2.1, щоб зробити current_newрівним букві, а потім currentзамість неї. Коли ми знову досягнемо цього кроку, ми закінчили.


1
У Python's for цикл є ще одне застереження. Це було б ідеальним випадком використання для цього.
Якубе

4

Python 2, 14952 14754 дзвінків

Дуже схожа на першу відповідь, але не намагається наступних символів, які призводять до неможливих підрядів, які:

  • ми знаємо, num_occurщо вони не зустрічаються в цілі (з попередніх дзвінків)

  • ми вже використовували підрядку частіше, ніж це відбувається відповідно до num_occur

(Додасть підрахунок подстрок в хвилину) зроблено

def get_that_string(h,l,alpha = "abc"):
    dic = {}
    s = ""
    ##costs 1 additional call per example, but its worth it
    char_list = [num_occur(j,h) for j in alpha[:-1]]
    char_list.append(l - sum(char_list))
    for y, z in zip(char_list,alpha):
        dic[z] = y
    end_reached = False
    while len(s) < l:
        for t in alpha:
            if not end_reached:
                neu_s = s + t
                substrings = [neu_s[i:]   for i in range(len(neu_s))]
            else:
                neu_s = t + s
                substrings = [neu_s[:i+1] for i in range(len(neu_s))]
            ## Test if we know that that can't be the next char
            all_in_d = [suff for suff in substrings if suff in dic.keys()]
            poss=all(map(dic.get,all_in_d))
            if poss:
                if not neu_s in dic.keys():
                    dic[neu_s] = num_occur(neu_s,h)
                if dic[neu_s] > 0:
                    s=neu_s
                    for suff in all_in_d:
                        dic[suff] -= 1
                    break
        else:
            end_reached = True
    ##print s
    return s


## test suite start
import urllib

def num_occur(needle, haystack):
    global calls
    calls += 1
    num = 0
    for i in range(len(haystack) - len(needle) + 1):
        if haystack[i : i + len(needle)] == needle:
            num += 1
    return num

calls = 0
url = "https://raw.githubusercontent.com/iatorm/random-data/master/abc_strings.txt"
inputs = urllib.urlopen(url).read().split("\n")
print "Check: ", inputs == map(lambda h: get_that_string(h, len(h)), inputs)
print "Calls: ", calls

4

Python 12705 12632 дзвінків

  1. складіть список подій із 2 рядків символів
  2. сортувати список
  3. спочатку побудуйте рядок, пробуючи найбільш ймовірний символ, не перевіряйте, чи є лише одна можливість
  4. оновити список
  5. якщо список порожній, він закінчується крок 2

Я використовував скелет функції Loovjo. Я ніколи не кодую в Python, мені потрібна булава

EDIT:
Доданий код для рядків довжиною одного символу
Доданий код, щоб відхилити вже відповідні шаблони

def finds(S):

    if len(S) == 0:
            return ""
    if len(S) == 1 
            if num_occur("a",S) == 1 :
                         return "a"
            if num_occur("b",S) == 1 :
                         return "b"
            return "c"
    tuples=[]
    alphabet=[ "aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb"]
    for s in alphabet : tuples.append( (num_occur(s,S), s) )

    sum=0
    for (i,s) in tuples :   sum+=i
    tuples.append( (len(S)-sum-1, "cc") )
    tuples.sort(key=lambda x:(-x[0],x[1]))

    (i, current) = tuples[0]
    tuples[0] = (i-1, current)

    add_at_start = True
    nomatch=[]
    while len(tuples) > 0:
            worked = False
            tuples.sort(key=lambda x:(-x[0],x[1]))
            count=0
            if not add_at_start :
                    for (n, s) in tuples :
                            if s[0]==current[-1:] :         count+=1
            for i in range(len(tuples)):
                    (n, s)=tuples[i]
                    if add_at_start:
                            if current[0] == s[1] :
                                    current_new = s[0] + current
                                    possible=True
                                    for nm in nomatch :
                                            lng=len(nm)
                                            if current_new[0:lng] == nm :
                                                    possible=False
                                                    break
                                    if possible and num_occur(current_new, S) > 0:
                                            current = current_new
                                            worked = True
                                    else :
                                            nomatch.append(current_new)
                    else:
                            if current[-1:] == s[0] :
                                    current_new =  current + s[1]
                                    possible=True
                                    for nm in nomatch :
                                            lng=len(nm)
                                            if current_new[-lng:] == nm :
                                                    possible=False
                                                    break
                                    if count == 1 or (possible and num_occur(current_new, S) > 0) :
                                            current = current_new
                                            worked = True
                                    else :
                                            nomatch.append(current_new)
                    if worked :
                            if n == 1:
                                    del tuples[i]
                            else    :
                                    tuples[i] = (n-1, s)
                            break
            if not worked:
                    add_at_start = False
    return current

у вашому алфавіті немає "cc"?
Спарр

@Sparr "cc" розраховано, це економить 100 дзвінків: `tuples.append ((len (S) -sum-1," cc "))`
Emmanuel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.