Inglese composito


28

Una parola composta è una parola che contiene 2 o più parole in essa. Possiamo fare di meglio, però. Abbiamo bisogno che tu crei 1 parola (senza senso) che contenga ogni parola .

Tuttavia, vogliamo che questa parola sia il più breve possibile. Possiamo usare lettere sovrapposte per raggiungere questo obiettivo.

Ad esempio, se il tuo elenco di parole fosse ["cat", "atom", "a"], vorrai tornare "catom".

Input Output

Il tuo programma dovrà prendere un elenco di parole come input e restituire una parola composta come output.

L'elenco di parole che userete sono le prime 10000 parole in inglese, secondo Google (se questo elenco risulta troppo semplice, posso cambiarlo in uno più lungo). Per riferimento, semplicemente aggiungendo ogni parola ti dà un punteggio di 65888.

Il tuo punteggio è il numero di lettere nella tua ultima parola, più basso è meglio. Il demolitore va al primo poster.



1
@Loovjo no, ma se finisce che il bruteforcing è abbastanza veloce da correre, allora cambierò l'elenco delle parole per allungarlo.
Nathan Merrill,

1
@PatrickRoberts Se si adatta alla tua risposta, oggetti di scena per te :) Un link pastebin / gist sarebbe fantastico, ma non è necessario.
Nathan Merrill,

1
Hmm, chi conosce un buon commesso viaggiatore asimmetrico euristico?
Dave,

2
Nessun avvolgimento e sì deterministico.
Nathan Merrill,

Risposte:


26

C ++, lunghezza della parola finale: 38272

(la versione ottimizzata ha richiesto circa 20 minuti)

#include <iostream>
#include <string>
#include <vector>

std::size_t calcOverlap(const std::string &a, const std::string &b, std::size_t limit, std::size_t minimal) {
    std::size_t la = a.size();
    for(std::size_t p = std::min(std::min(la, b.size()), limit + 1); -- p > minimal; ) {
        if(a.compare(la - p, p, b, 0, p) == 0) {
            return p;
        }
    }
    return 0;
}

int main() {
    std::vector<std::string> words;

    // Load all words from input
    while(true) {
        std::string word;
        std::getline(std::cin, word);
        if(word.empty()) {
            break;
        }
        words.push_back(word);
    }

    std::cerr
        << "Input word count: " << words.size() << std::endl;

    // Remove all fully subsumed words

    for(auto p = words.begin(); p != words.end(); ) {
        bool subsumed = false;
        for(auto i = words.begin(); i != words.end(); ++ i) {
            if(i == p) {
                continue;
            }
            if(i->find(*p) != std::string::npos) {
                subsumed = true;
                break;
            }
        }
        if(subsumed) {
            p = words.erase(p);
        } else {
            ++ p;
        }
    }

    std::cerr
        << "After subsuming checks: " << words.size()
        << std::endl;

    // Sort words longest-to-shortest (not necessary but doesn't hurt. Makes finding maxlen a tiny bit easier)
    std::sort(words.begin(), words.end(), [](const std::string &a, const std::string &b) {
        return a.size() > b.size();
    });

    std::size_t maxlen = words.front().size();

    // Repeatedly combine most-compatible words until there is only one left
    std::size_t bestPossible = maxlen - 1;
    while(words.size() > 1) {
        auto bestA = words.begin();
        auto bestB = -- words.end();
        std::size_t bestOverlap = 0;
        for(auto p = ++ words.begin(), e = words.end(); p != e; ++ p) {
            if(p->size() - 1 <= bestOverlap) {
                continue;
            }
            for(auto q = words.begin(); q != p; ++ q) {
                std::size_t overlap = calcOverlap(*p, *q, bestPossible, bestOverlap);
                if(overlap > bestOverlap) {
                    bestA = p;
                    bestB = q;
                    bestOverlap = overlap;
                }
                overlap = calcOverlap(*q, *p, bestPossible, bestOverlap);
                if(overlap > bestOverlap) {
                    bestA = q;
                    bestB = p;
                    bestOverlap = overlap;
                }
            }
            if(bestOverlap == bestPossible) {
                break;
            }
        }
        std::string newStr = std::move(*bestA);
        newStr.append(*bestB, bestOverlap, std::string::npos);

        if(bestA == -- words.end()) {
            words.pop_back();
            *bestB = std::move(words.back());
            words.pop_back();
        } else {
            *bestB = std::move(words.back());
            words.pop_back();
            *bestA = std::move(words.back());
            words.pop_back();
        }

        // Remove any words which are now in the result
        for(auto p = words.begin(); p != words.end(); ) {
            if(newStr.find(*p) != std::string::npos) {
                std::cerr << "Now subsumes: " << *p << std::endl;
                p = words.erase(p);
            } else {
                ++ p;
            }
        }

        std::cerr
            << "Words remaining: " << (words.size() + 1)
            << " Latest combination: (" << bestOverlap << ") " << newStr
            << std::endl;

        words.push_back(std::move(newStr));
        bestPossible = bestOverlap; // Merging existing words will never make longer merges possible
    }

    std::string result = words.front();

    std::cout
        << result
        << std::endl;
    std::cerr
        << "Word size: " << result.size()
        << std::endl;
    return 0;
}

Verifica bash one-liner:

cat commonwords.txt | while read p; do grep "$p" merged.txt >/dev/null || echo "Not found: $p"; done

Ha anche prodotto alcune parole in corso piuttosto interessanti. Ecco alcuni dei miei preferiti:

  • Polyesterday (nostalgia sintetica)
  • afghanistanbul (direbbe [inserire un politico che non ti piace])
  • togethernet (internet amichevole)
  • elefante (un grande fantasma)
  • thundergroundwaterproof (come in "Non so perché abbiano sentito il bisogno di renderlo thundergroundwaterproof, ma mi rende nervoso")

E:

  • codescribingo (forse una sfida imminente su questo sito?)

L'output finale è su pastebin qui: http://pastebin.com/j3qYb65b


2
Un'osservazione che potrebbe essere utile per gli altri che cercano di ottenere la soluzione ottimale: il problema può essere ridotto a un problema del commesso viaggiatore non euclideo asimmetrico: definire una matrice in cui l'elemento i, j = max_word_length - overlap(word[i], word[j])(dove overlapcontrolla la sovrapposizione dalla destra del primo argomento a sinistra del secondo). Risolvendo questo (buona fortuna!), Quindi tagliando il ciclo risultante al costo più elevato (sovrapposizione più bassa) si otterrà un elenco ordinato di parole che possono essere unite per dare una soluzione ottimale.
Dave,

Impressionante. Questo sta controllando ogni parola nella lista corrente contro ogni altra, ogni volta attraverso? Stavo considerando questo, ma ho pensato che avrei dovuto controllare un campione casuale per farlo funzionare in un tempo ragionevole.
trichoplax,

1
@ValueInk sì, la memorizzazione nella cache sarebbe un notevole aumento delle prestazioni. Una versione precedente lo aveva, ma aggiunge molta complessità, quindi quando ho adattato un po 'di logica ho dovuto riscrivere il lotto. Invece ho scelto di abbandonare la cache. Inoltre no, questo non è del tutto ottimale. È un algoritmo avido, quindi non può giudicare i compromessi e non è in grado di scegliere tra opzioni "ugualmente buone". Vedi il mio commento su TSP per la soluzione ottimale (NP-Hard).
Dave,

1
@trichoplax yup, ecco cosa sta facendo. Il tempo di esecuzione è O (n ^ 3), che non è poi così male per questa dimensione del campione. Con la memorizzazione nella cache potrebbe essere ridotto a O (n ^ 2). Il controllo interno è anche molto parallelo, quindi anche per campioni più grandi potrebbe essere eseguito in tempi ragionevoli con il threading / calcolo distribuito. Anche questo ottiene un grande aumento di velocità dalla conoscenza della gamma di possibili larghezze di sovrapposizione per ogni passaggio, che riduce il tempo di esecuzione di un fattore 10.
Dave

2
Questo potrebbe non essere difficile come TSP generale, perché abbiamo il vincolo divertente che si sovrappongono (a, b) ≥ min {sovrapposizione (a, d), sovrapposizione (c, d), sovrapposizione (c, b)} per tutti a , b, c, d.
Anders Kaseorg,

21

C ++ 11, 38272 lettere, dimostrato ottimale

Questo algoritmo è garantito per fornire un limite inferiore alla soluzione. In questo caso, è in grado di raggiungere il limite inferiore e produrre una soluzione ottimale di 38272 lettere. (Questo corrisponde alla soluzione trovata dall'algoritmo goloso di Dave. Sono rimasto sorpreso e un po 'deluso nello scoprire che è ottimale, ma eccoci qui.)

Funziona risolvendo il problema del flusso di costo minimo sulla rete creato come segue.

  • Innanzitutto, tutte le parole contenute in altre parole sono ridondanti; scartali.
  • Per ogni parola w , disegna due nodi w _0 e w _1, dove w _0 è un'origine con capacità 1 e w _1 è un sink con capacità 1.
  • Per ogni prefisso (rigoroso) o suffisso a di qualsiasi parola, disegna un nodo a .
  • Per ogni suffisso a di w , traccia un arco da w _0 a a con capacità 1 e costo 0.
  • Per ogni prefisso a di w , traccia un arco da a a w _1 con capacità 1 e lunghezza del costo ( w ) - lunghezza ( a ).

Qualsiasi stringa di lunghezza n che contiene ogni parola può essere convertita in un flusso su questa rete con un costo al massimo n . Pertanto, il flusso di costo minimo su questa rete è un limite inferiore per la lunghezza della stringa più corta.

Se siamo fortunati, e in questo caso lo siamo, allora dopo aver reindirizzato il flusso che entra in w _1 di nuovo da w _0, troveremo un flusso ottimale che ha solo un componente collegato e che passa attraverso il nodo per il vuoto stringa. In tal caso, conterrà un circuito euleriano che inizia e termina lì. Tale circuito euleriano può essere letto come una stringa di lunghezza ottimale.

Se non siamo stati fortunati, aggiungi alcuni archi extra tra la stringa vuota e le stringhe più corte negli altri componenti collegati al fine di garantire l'esistenza di un circuito euleriano. La stringa non sarebbe più necessariamente ottimale in quel caso.

Uso la libreria LEMON per il flusso di costo minimo e gli algoritmi di circuito di Eulerian. (Questa è stata la prima volta che ho usato questa libreria e sono rimasto impressionato, lo userò sicuramente di nuovo per le future esigenze degli algoritmi grafici.) LEMON viene fornito con quattro diversi algoritmi di flusso a costo minimo; li si può provare qui con --net, --cost, --cap, e --cycle(default).

Il programma viene eseguito in 0,5 secondi , producendo questa stringa di output .

#include <iostream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include <lemon/core.h>
#include <lemon/connectivity.h>
#include <lemon/euler.h>
#include <lemon/maps.h>
#include <lemon/list_graph.h>
#include <lemon/network_simplex.h>
#include <lemon/cost_scaling.h>
#include <lemon/capacity_scaling.h>
#include <lemon/cycle_canceling.h>

using namespace std;

typedef lemon::ListDigraph G;

struct Word {
    G::Node suffix, prefix;
    G::Node tour_node;
};

struct Edge {
    unordered_map<string, Word>::iterator w;
    G::Arc arc;
};

struct Affix {
    vector<Edge> suffix, prefix;
    G::Node node;
    G::Node tour_node;
};

template<class MCF>
bool solve(const G &net, const G::ArcMap<int> &lowerMap, const G::ArcMap<int> &upperMap, const G::ArcMap<int> &costMap, const G::NodeMap<int> &supplyMap, int &totalCost, G::ArcMap<int> &flowMap)
{
    MCF mcf(net);
    if (mcf.lowerMap(lowerMap).upperMap(upperMap).costMap(costMap).supplyMap(supplyMap).run() != mcf.OPTIMAL)
        return false;
    totalCost = mcf.totalCost();
    mcf.flowMap(flowMap);
    return true;
}

int main(int argc, char **argv)
{
    clog << "Reading dictionary from stdin" << endl;
    unordered_map<string, Affix> affixes;
    unordered_map<string, Word> words;
    unordered_set<string> subwords;
    G net, tour;
    G::ArcMap<int> lowerMap(net), upperMap(net), costMap(net);
    G::NodeMap<int> supplyMap(net);
    string new_word;
    while (getline(cin, new_word)) {
        if (subwords.find(new_word) != subwords.end())
            continue;
        for (auto i = new_word.begin(); i != new_word.end(); ++i) {
            for (auto j = new_word.end(); j != i; --j) {
                string s(i, j);
                words.erase(s);
                subwords.insert(s);
            }
        }
        words.emplace(new_word, Word());
    }
    for (auto w = words.begin(); w != words.end(); ++w) {
        w->second.suffix = net.addNode();
        supplyMap.set(w->second.suffix, 1);
        w->second.prefix = net.addNode();
        supplyMap.set(w->second.prefix, -1);
        for (auto i = w->first.begin(); ; ++i) {
            affixes.emplace(string(w->first.begin(), i), Affix()).first->second.prefix.push_back(Edge {w});
            affixes.emplace(string(i, w->first.end()), Affix()).first->second.suffix.push_back(Edge {w});
            if (i == w->first.end())
                break;
        }
        w->second.tour_node = tour.addNode();
    }
    for (auto a = affixes.begin(); a != affixes.end();) {
        if (a->second.suffix.empty() || a->second.prefix.empty() ||
            (a->second.suffix.size() == 1 && a->second.prefix.size() == 1 &&
             a->second.suffix.begin()->w == a->second.prefix.begin()->w)) {
            affixes.erase(a++);
        } else {
            a->second.node = net.addNode();
            supplyMap.set(a->second.node, 0);
            for (auto &e : a->second.suffix) {
                e.arc = net.addArc(e.w->second.suffix, a->second.node);
                lowerMap.set(e.arc, 0);
                upperMap.set(e.arc, 1);
                costMap.set(e.arc, 0);
            }
            for (auto &e : a->second.prefix) {
                e.arc = net.addArc(a->second.node, e.w->second.prefix);
                lowerMap.set(e.arc, 0);
                upperMap.set(e.arc, 1);
                costMap.set(e.arc, e.w->first.length() - a->first.length());
            }
            a->second.tour_node = lemon::INVALID;
            ++a;
        }
    }

    clog << "Read " << words.size() << " words and found " << affixes.size() << " affixes; ";
    clog << "created network with " << countNodes(net) << " nodes and " << countArcs(net) << " arcs" << endl;

    int totalCost;
    G::ArcMap<int> flowMap(net);
    bool solved;
    if (argc > 1 && string(argv[1]) == "--net") {
        clog << "Using network simplex algorithm" << endl;
        solved = solve<lemon::NetworkSimplex<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
    } else if (argc > 1 && string(argv[1]) == "--cost") {
        clog << "Using cost scaling algorithm" << endl;
        solved = solve<lemon::CostScaling<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
    } else if (argc > 1 && string(argv[1]) == "--cap") {
        clog << "Using capacity scaling algorithm" << endl;
        solved = solve<lemon::CapacityScaling<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
    } else if ((argc > 1 && string(argv[1]) == "--cycle") || true) {
        clog << "Using cycle canceling algorithm" << endl;
        solved = solve<lemon::CycleCanceling<G>>(net, lowerMap, upperMap, costMap, supplyMap, totalCost, flowMap);
    }

    if (!solved) {
        clog << "error: no solution found" << endl;
        return 1;
    }
    clog << "Lower bound: " << totalCost << endl;

    G::ArcMap<string> arcLabel(tour);
    G::Node empty = tour.addNode();
    affixes.find("")->second.tour_node = empty;
    for (auto &a : affixes) {
        for (auto &e : a.second.suffix) {
            if (flowMap[e.arc]) {
                if (a.second.tour_node == lemon::INVALID)
                    a.second.tour_node = tour.addNode();
                arcLabel.set(tour.addArc(e.w->second.tour_node, a.second.tour_node), "");
            }
        }
        for (auto &e : a.second.prefix) {
            if (flowMap[e.arc]) {
                if (a.second.tour_node == lemon::INVALID)
                    a.second.tour_node = tour.addNode();
                arcLabel.set(tour.addArc(a.second.tour_node, e.w->second.tour_node), e.w->first.substr(a.first.length()));
            }
        }
    }

    clog << "Created tour graph with " << countNodes(tour) << " nodes and " << countArcs(tour) << " arcs" << endl;

    G::NodeMap<int> compMap(tour);
    int components = lemon::stronglyConnectedComponents(tour, compMap);
    if (components != 1) {
        vector<unordered_map<string, Affix>::iterator> breaks(components, affixes.end());
        for (auto a = affixes.begin(); a != affixes.end(); ++a) {
            if (a->second.tour_node == lemon::INVALID)
                continue;
            int c = compMap[a->second.tour_node];
            if (c == compMap[empty])
                continue;
            auto &b = breaks[compMap[a->second.tour_node]];
            if (b == affixes.end() || b->first.length() > a->first.length())
                b = a;
        }
        int offset = 0;
        for (auto &b : breaks) {
            if (b != affixes.end()) {
                arcLabel.set(tour.addArc(empty, b->second.tour_node), b->first);
                arcLabel.set(tour.addArc(b->second.tour_node, empty), "");
                offset += b->first.length();
            }
        }
        clog << "warning: Found " << components << " components; solution may be suboptimal by up to " << offset << " letters" << endl;
    }

    if (!lemon::eulerian(tour)) {
        clog << "error: failed to make tour graph Eulerian" << endl;
        return 1;
    }

    for (lemon::DiEulerIt<G> e(tour, empty); e != lemon::INVALID; ++e)
        cout << arcLabel[e];
    cout << endl;

    return 0;
}

Anche se non posso pretendere di capire come funziona il flusso minimo, se questo è davvero ottimale, ben fatto!
Nathan Merrill,

1
Mi dispiace deludere: PI non aveva pensato a una rete di flusso; è piuttosto elegante. Mi ci è voluto un po 'per capire come stavi collegando le parole insieme nella tua rete prima che mi rendessi finalmente conto "Per ogni prefisso (rigoroso) o suffisso a di qualsiasi parola, disegna un nodo un" significa che i nodi "a" possono essere condivisi tra più parole.
Dave,

1
Inoltre la tua soluzione è circa 2000 volte più veloce della mia!
Dave,

1
Forse aiutare questo ragazzo ( cs.cmu.edu/~tom7/portmantout ) con il suo tentativo di fare una cosa simile?
Oliver Daugherty-Long

2
@ OliverDaugherty-Long Done ! ( Davvero questa volta.) I limiti più noti in precedenza erano 520732 ≤ lunghezza ottimale ≤ 537136 e credo di aver migliorato entrambi i limiti a 536186.
Anders Kaseorg

13

Java 8, ~ 5 minuti, lunghezza 39.279

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class Words {

    public static void main(String[] args) throws Throwable {
        File file = new File("words.txt");
        List<String> wordsList = new ArrayList<>();
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line;
        while ((line = reader.readLine()) != null) {
            wordsList.add(line);
        }
        reader.close();

        Set<String> words = new HashSet<>();

        System.out.println("Finished I/O");

        for (int i = 0; i < wordsList.size(); i++) { //Step 1: remove any words that occur in other words
            boolean in = false;
            for (int j = 0; j < wordsList.size(); j++) {
                if (i != j && wordsList.get(j).contains(wordsList.get(i))) {
                    in = true;
                    break;
                }
            }
            if (!in) {
                words.add(wordsList.get(i));
            }
        }

        System.out.println("Removed direct containers");

        List<String> working = words.stream().sorted((c, b) -> Integer.compare(c.length(), b.length())).collect(Collectors.toList()); //Sort by length, shortest first
        StringBuilder result = new StringBuilder();
        result.append(working.get(0));
        while (!working.isEmpty()) {
            Optional<String> target = working.stream().sorted((c, b) -> Integer.compare(firstLastCommonality(result.toString(), b), firstLastCommonality(result.toString(), c))).findFirst(); //Find the string that has the greatest in common with the end of 'result'
            if(target.isPresent()) { //It really should be present, but just in case
                String s = target.get();
                working.remove(s);
                int commonality = firstLastCommonality(result.toString(), s);
                s = s.substring(commonality);
                result.append(s);
            }
        }

        System.out.println("Finished algorithm");

        String r = result.toString();
        System.out.println("The string: \n" + r);
        System.out.println("Length: \n" + r.length());
        System.out.println("Verified: \n" + !wordsList.stream().filter(s -> !r.contains(s)).findFirst().isPresent());
    }

    private static int firstLastCommonality(String a, String b) {
        int count = 0;
        int len = b.length();
        while (!a.endsWith(b) && !b.equals("")) {
            b = cutLastChar(b);
            count++;
        }
        return len - count;
    }

    private static String cutLastChar(String string) {
        if (string.length() - 1 < 0) {
            return string;
        } else {
            return string.substring(0, string.length() - 1);
        }
    }

}

Ingresso:

  • un file chiamato 'words.txt' nella directory di lavoro, nello stesso identico formato del file nel post principale

Produzione:

Finished I/O
Removed direct containers
Finished algorithm
The string: 
[Moved to pastebin](http://pastebin.com/iygyR3zL)
Length: 
39279
Verified: 
true

2
FGITW, e in Java non meno. Signore, avete il mio voto.
Patrick Roberts,

2
Bello! Ti sei sbarazzato dei 26,609personaggi.
R. Kap

@ R.Kap vai a capire! Non pensavo nemmeno di calcolarlo ... Dev'esserci però un algoritmo più intelligente, questa è solo la prima cosa che mi è venuta in mente ...
Socratic Phoenix

7

Python 2, 39254 caratteri

Richiede 1-2 minuti per essere eseguito sulla mia macchina, funziona prendendo la parola più lunga e quindi aggiungendo sempre la parola alla stringa del risultato che ha più stringhe in comune. (Prima di allora tutte le parole che sono sottostringhe di altre parole vengono rimosse per impedire l'aggiunta non necessaria alla stringa.)

Aggiornamento: ho cercato di guardare in entrambe le direzioni, ma non è meglio. (forse sta usando parole che possono essere utilizzate meglio in seguito?)

Link alla parola su pastebin.

primi 100 caratteri:

telecommunicationsawayneillegallynnbabyenbcjrxltdxmlbsrcwvtxxxboxespnycdsriconsentencessexyrsslipodc

Codice:

import re
import urllib

def suffix_dist(w1,w2):
    for i in range(min(map(len,[w1,w2])))[::-1]:
        if w1[-i:]==w2[:i]:
            return i
    return 0

url="https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english.txt"
s=urllib.urlopen(url).read()
words=s.split()
words.sort(key=len,reverse=True)

## remove words that are substrings of other words anyway
for i in range(len(words))[::-1]:
    if any(words[i] in w for w in words[:i]):
        words.pop(i)

print len(words)

words.sort(key=len)
w1=words.pop(-1)
result=w1
while words:
    ## get the word with longest shared suffix/prefix
    w2=max(words,key=lambda x:suffix_dist(w1,x))
    print w2
    words.pop(words.index(w2))
    if w2 in result:
        break
    result+=w2[suffix_dist(w1,w2):]
    w1=w2


print result[:100]
print len(result)
print "Test:", all(w in result for w in s.split())

2
Welp, sono stato battuto da 25 personaggi ... +1 per quello
Socratic Phoenix

Bel lavoro! Ho avuto un'idea simile ma hai già avuto una risposta. La mia versione inizia con una parola piccola invece di una parola grande, più qualche altra potatura che fa perdere drasticamente il fattore tempo, impiegando fino a 3 volte il tempo di esecuzione.
Value Ink

5

Rubino, 39222 caratteri

Utilizza un approccio simile a @KarlKastor nella sua risposta Python, ma la stringa iniziale è una delle parole più piccole anziché la più grande. Un'altra ottimizzazione (non so quanto sia di aiuto) è che tra ogni aggiunta, elimina tutte le parole che potrebbero essere già state incluse nella stringa a causa di parole sovrapposte.

Funziona in poco più di 4 minuti sulla mia macchina, senza contare la richiesta web per recuperare l'elenco di parole, ma non abbastanza 4:20.

La parola su Pastebin.

require 'net/http'

puts "Obtaining word list..."
data = Net::HTTP.get(URI'https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english.txt')
puts "Word list obtained!"

puts "Starting calculation..."
time = Time.now

words = data.split.sort_by(&:size)
words.reject!{|w| words.find{|x| w!=x && x.include?(w)}}

string = words.shift

def merge_dist(prefix, suffix)
    [prefix.size,suffix.size].min.downto(0).find{|i| prefix.end_with?(suffix[0,i])}
end

while words.length > 0
    suffix = words.max_by{|w| merge_dist string, w}
    string += suffix[merge_dist(string, suffix)..-1]
    words.reject!{|w| string.include? w}
end

delta = Time.now - time

puts "Calculation completed in #{delta} seconds!"
puts "Word is #{string.size} chars long."

open("word.txt", 'w') << string

puts "Word saved to word.txt"

3

PowerShell v2 +, 46152 caratteri

param([System.Collections.ArrayList]$n)$n=$n|sort length -des;while($n){$x=$n.Count;$o+=$n[0];0..$x|%{if($o.IndexOf($n[$_])-ge0){$n.RemoveAt($_)}}}$o

Prende l'input come un elenco, lo inserisce in un ArrayList (in modo da poterlo manipolare). Ci sortsi da lengthin -desordine cending. Quindi, whileabbiamo ancora parole nel nostro array di input , eseguiamo un loop. Ogni iterazione, imposta l'helper $xin modo che sia uguale a quanti ne abbiamo rimasti, attacca all'elemento successivo nell'elenco per il nostro output $o, quindi cerca tra i vari elementi ancora presenti nell'elenco. Se il .IndexOfnon è uguale a -1(cioè, la parola è stata trovata da qualche parte in $o), rimuoviamo quella parola dal nostro elenco di parole rimanenti. Alla fine, alla fine, uscita $o.

Non ho accesso a un Pastebin o simile, quindi ecco l'inizio e la fine della parola per temporaneo - telecommunicationscharacterizationresponsibilitiessublimedirectory...fcmxvtwvfxwujmjsuhjjrxjdbkdxqc. Il che immagino abbia rasato circa 20.000 caratteri dall'input, quindi non così male, suppongo.

Sto lavorando su perfezionamenti.


0

PHP 46612 caratteri

Questo è solo un inizio Spero di migliorarlo. Tutto quello che ho fatto finora è rimuovere qualsiasi parola che sia una sottostringa di un'altra parola. Sto lavorando su 3 copie dell'array, ma la memoria non sembra essere un problema.

<?php
set_time_limit(3000);

$words=file('https://raw.githubusercontent.com/first20hours/google-10000-english/master/google-10000-english.txt');
$words = array_map('trim', $words);

usort($words, function ($a, $b)
{
    if (strlen($a) == strlen($b) ){
        return 0;
    }
    return ( strlen($a) < strlen($b) )? -1 : 1;
});

$final_array=$words;
$comparison_array=$words;


  foreach($words as $key => $word){
    echo $word."<br>\n";
      foreach($comparison_array as $nestedKey => $nestedWord){
          if (strlen($nestedWord) <= strlen($word)) {
            unset($comparison_array[$nestedKey]);
            continue;
          }
          if( strpos($nestedWord,$word) !== FALSE ){
              unset($final_array[$key]);
              $restart=true;
              break;
          } 
      }    
  }


sort($final_array);
$compound='';
$compound = implode($final_array);
echo $compound;
echo "  <br><br>\n\n". strlen($compound);
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.