Identifica una stringa dalle sue sottostringhe


20

introduzione

In precedenza ho creato due sfide in cui l'idea è quella di ricostruire un oggetto utilizzando il minor numero possibile di operazioni di tipo query; questo sarà il terzo.

L'obiettivo

I tuoi input devono essere una stringa non vuota Ssull'alfabeto abce la sua lunghezza e il tuo output deve essere S. Senza restrizioni, questo sarebbe ovviamente un compito banale; il problema è che non ti è permesso accedere Sdirettamente. L'unica cosa che ti è permesso fare Sè chiamare la funzione num_occur(T, S), dove Tc'è qualche altra stringa, e num_occurconta il numero di occorrenze di Tin S. Le occorrenze sovrapposte sono considerate distinte, quindi num_occur(T, S)restituisce davvero il numero di indici in imodo tale

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

Ad esempio, num_occur("aba", "cababaababb")tornerà 3. Nota anche che num_occur(S, S)tornerà 1. Il risultato di num_occur("", S)non è definito e non si deve chiamare la funzione su una stringa vuota.

In breve, dovresti scrivere una funzione o un programma che accetta Se length(S)come input, chiama num_occuralcune stringhe più brevi e un Scerto numero di volte, ricostruisce Sda tali informazioni e le restituisce.

Regole e punteggio

Il tuo obiettivo è scrivere un programma che effettui il minor numero di chiamate num_occurpossibile. In questo repository , troverai un file chiamato abc_strings.txt. Il file contiene 100 stringhe, ognuna sulla propria linea, tra le lunghezze 50 e 99. Il tuo punteggio è il numero totale di chiamate a num_occursu questi input , il punteggio più basso è migliore. La soluzione preferibilmente terrà traccia di questo numero durante l'esecuzione e lo stamperà al termine. Le stringhe vengono generate scegliendo lettere uniformemente casuali da abc; puoi ottimizzare per questo metodo di generazione delle stringhe, ma non delle stringhe stesse.

Non vi è alcun limite di tempo, tranne per il fatto che è necessario eseguire la soluzione sui casi di test prima di inviarlo. La soluzione dovrebbe funzionare per qualsiasi input valido S, non solo per i casi di test.

Sei incoraggiato a condividere anche la tua implementazione num_occur, se non stai usando qualcun altro. Per far rotolare la palla, ecco un'implementazione in 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

I nostri algoritmi devono funzionare per tutte le stringhe possibili So solo per i casi di test?
Loovjo,

@Loovjo Bella domanda. Dovrebbero teoricamente funzionare per tutte le stringhe non vuote. Modificherò la sfida.
Zgarb,

all non-empty stringsdi qualunque lunghezza?
edc65,

@ edc65 Teoricamente sì. È possibile ignorare indirizzi di memoria limitati e altre limitazioni pratiche.
Zgarb,

È possibile aggiungere un algoritmo VW per superare il test di valutazione con esito positivo: verificare prima che si verifichino le stringhe note di abc_strings.txt
Emmanuel,

Risposte:


6

Javascript, 14325 14311 chiamate

Iniziamo con una stringa vuota e procediamo in modo ricorsivo aggiungendo una nuova lettera alla fine o all'inizio della stringa corrente mentre abbiamo ancora almeno una corrispondenza.

Tutti i risultati precedenti numOccur()vengono salvati symnell'oggetto e utilizziamo questi dati per rifiutare immediatamente qualsiasi nuova stringa che non può essere candidata.

EDIT : Poiché iniziamo sempre con 'a', conosciamo sempre il numero esatto di anella stringa. Utilizziamo queste informazioni per terminare il processo prima quando rileviamo che amanca solo una sequenza di . Risolto anche l'espressione regolare che non era valida in Chrome e 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");

Snippet completo eseguibile di seguito.


"In breve, dovresti scrivere una funzione o un programma che [...] ricostruisce S da quell'informazione e la restituisca ."
KarlKastor,

@KarlKastor - Oops. Hai ragione. Questo è stato risolto. Grazie!
Arnauld,

4

Python, 15205 chiamate

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 

Questo invio è molto probabilmente non ottimale, perché utilizza solo num_occurper verificare se una stringa è una sottostringa Se non la utilizza mai per contare effettivamente la quantità di sottostringhe.

L'algoritmo funziona memorizzando una stringa currentche è costruita per essere uguale alla Sfine. Ecco tutti i passaggi dell'algoritmo:

  1. Abbiamo impostato currentuguale a''

  2. Passa attraverso ogni lettera dell'alfabeto e procedi come segue:

    2.1. Crea una nuova stringa current_newe impostala uguale a currentseguita dalla lettera.

    2.2. Controlla se current_newè incluso Seseguendolo num_occure vedi se il risultato è maggiore di uno.

    2.3. Se current_newè incluso in S, impostare currentsu current_newe tornare al passaggio 2. Altrimenti, si passa alla lettera successiva.

  3. Se la lunghezza di currentè uguale alla lunghezza di Spossiamo dire che abbiamo finito. Altrimenti, torniamo al passaggio 2, ma modificiamo il passaggio 2.1 per renderlo current_newuguale alla lettera seguita da currentinvece. Quando raggiungiamo di nuovo questo passaggio, abbiamo finito.


1
Python's for loop ha una clausola else. Questo sarebbe un caso d'uso perfetto per questo.
Jakube,

4

Python 2, 14952 14754 chiamate

Molto simile alla prima risposta, ma non prova i caratteri successivi che risultano in sottostringhe impossibili che:

  • sappiamo num_occurche non si verificano nel target (dalle chiamate precedenti)

  • abbiamo già usato la sottostringa più spesso di quanto non avvenga secondo num_occur

(aggiungerà il conteggio delle sottostringhe in un minuto) fatto

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

Chiamate Python 12705 12632

  1. crea un elenco di occorrenze di stringhe di 2 caratteri
  2. ordina l'elenco
  3. costruisci la stringa provando prima il carattere più probabile, non testare se esiste una sola possibilità
  4. aggiorna l'elenco
  5. se l'elenco è vuoto, è terminato altrimenti passaggio 2

Ho usato lo scheletro della funzione Loovjo. Non ho mai scritto codice in Python, mi serviva uno scarponcino

EDIT:
codice aggiunto per stringhe di lunghezza di un carattere
Codice aggiunto per rifiutare i pattern già associati

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

no "cc" nel tuo alfabeto?
Sparr,

Viene calcolato @Sparr "cc", che consente di risparmiare 100 chiamate: `tuples.append ((len (S) -sum-1," cc "))`
Emmanuel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.