Inglés compuesto

28

Una palabra compuesta es una palabra que contiene 2 o más palabras. Sin embargo, podemos hacerlo mejor que eso. Necesitamos que cree 1 palabra (sin sentido) que contenga cada palabra .

Sin embargo, queremos que esta palabra sea lo más breve posible. Podemos usar letras superpuestas para lograr esto.

Por ejemplo, si su lista de palabras era ["cat", "atom", "a"], desearía regresar "catom".

De entrada y salida

Su programa necesitará tomar una lista de palabras como entrada y devolver una palabra compuesta como salida.

La lista de palabras que usará son las 10000 palabras principales en inglés, según Google (si esta lista resulta ser demasiado fácil, puedo cambiarla por una más larga). Como referencia, simplemente agregar cada palabra le da un puntaje de 65888.

Su puntaje es el número de letras en su palabra final, menor es mejor. El desempate va al primer póster.

Nathan Merrill
fuente
1
@Loovjo no, pero si resulta que la fuerza bruta es lo suficientemente rápida como para ejecutarse, entonces cambiaré la lista de palabras para que sea más larga.
Nathan Merrill
1
@PatrickRoberts Si encaja en su respuesta, le apoyamos :) Un enlace pastebin / gist sería genial, pero no es obligatorio.
Nathan Merrill el
1
Hmm, ¿quién conoce a un buen vendedor de viajes asimétrico heurístico?
Dave
2
Sin envoltura, y sí a determinista.
Nathan Merrill

Respuestas:

26

C ++, longitud final de la palabra: 38272

(La versión optimizada tardó unos 20 minutos)

#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;
}

Verificación bash one-liner:

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

También produjo algunas palabras geniales en progreso. Aquí están algunos de mis favoritos:

  • polyesterday (nostalgia sintética)
  • Afganistán (diría algo [insertar político que no le guste])
  • togethernet (internet amigable)
  • elephantom (un gran fantasma)
  • resistente al agua subterránea (como en "No sé por qué sintieron la necesidad de hacerlo resistente al agua subterránea, pero me pone nervioso")

Y:

  • codecribingo (¿tal vez un próximo desafío en este sitio?)

El resultado final está en pastebin aquí: http://pastebin.com/j3qYb65b

Dave
fuente
2
Una observación que podría ser útil para otros que buscan obtener la solución óptima: el problema puede reducirse a un problema de vendedor ambulante asimétrico no euclidiano: defina una matriz donde el elemento i, j = max_word_length - overlap(word[i], word[j])(donde overlapverifica la superposición desde la derecha de la primer argumento a la izquierda del segundo). Resolver esto (¡buena suerte!) Y luego cortar el ciclo resultante al costo más alto (superposición más baja) dará una lista ordenada de palabras que se pueden combinar para dar una solución óptima.
Dave
Impresionante. ¿Esto verifica cada palabra de la lista actual entre sí, cada vez? Estaba considerando esto, pero asumí que tendría que verificar una muestra aleatoria para que se ejecute en un tiempo razonable.
trichoplax
1
@ValueInk sí, el almacenamiento en caché sería un gran impulso para el rendimiento. Una versión anterior tenía eso, pero agrega mucha complejidad, por lo que cuando adapté algo de lógica tuve que volver a escribir el lote. En cambio, elegí dejar caer el almacenamiento en caché. Además, no, esto no es completamente óptimo. Es un algoritmo codicioso, por lo que no puede juzgar las compensaciones y no puede elegir entre opciones "igualmente buenas". Vea mi comentario TSP para la solución óptima (NP-Hard).
Dave
1
@trichoplax sí, eso es lo que está haciendo. El tiempo de ejecución es O (n ^ 3), que no es tan malo para este tamaño de muestra. Con el almacenamiento en caché se podría reducir a O (n ^ 2). La verificación interna también es muy paralela, por lo que incluso para muestras más grandes podría ejecutarse en un tiempo razonable con subprocesos / cálculo distribuido. Además, esto aumenta la velocidad al conocer el rango de posibles anchuras de superposición para cada paso, lo que reduce el tiempo de ejecución en un factor de 10.
Dave
2
Esto podría no ser tan difícil como el TSP general, porque tenemos la extraña restricción de que se superponen (a, b) ≥ min {superposición (a, d), superposición (c, d), superposición (c, b)} para todas las , b, c, d.
Anders Kaseorg el
21

C ++ 11, 38272 letras, probado óptimo

Este algoritmo está garantizado para proporcionar un límite inferior en la solución. En este caso, puede lograr el límite inferior y generar una solución óptima de 38272 letras. (Esto coincide con la solución encontrada por el codicioso algoritmo de Dave. Me sorprendió y me decepcionó un poco descubrir que es óptimo, pero ahí estamos).

Funciona resolviendo el problema de flujo de costo mínimo en la red construida de la siguiente manera.

  • Primero, cualquier palabra contenida en otras palabras es redundante; descartarlos.
  • Para cada palabra w , dibuje dos nodos w _0 y w _1, donde w _0 es una fuente con capacidad 1 y w _1 es un sumidero con capacidad 1.
  • Para cada prefijo o sufijo (estricto) a de cualquier palabra, dibuje un nodo a .
  • Para cada sufijo a de w , dibuje un arco de w _0 a a con capacidad 1 y cueste 0.
  • Para cada prefijo a de w , dibuje un arco desde a hasta w _1 con capacidad 1 y costo longitud ( w ) - longitud ( a ).

Cualquier cadena de longitud n que contenga cada palabra se puede convertir en un flujo en esta red con un costo máximo de n . Por lo tanto, el flujo de costo mínimo en esta red es un límite inferior en la longitud de la cadena más corta.

Si somos afortunados, y en este caso lo somos, entonces, después de redirigir el flujo que entra en w _1 fuera de w _0, encontraremos un flujo óptimo que solo tiene un componente conectado y que pasa a través del nodo para el vacío cuerda. Si es así, contendrá un circuito euleriano que comienza y termina allí. Tal circuito euleriano se puede volver a leer como una cadena de longitud óptima.

Si no tuvimos suerte, agregue algunos arcos adicionales entre la cadena vacía y las cadenas más cortas en los otros componentes conectados para asegurar que exista un circuito euleriano. La cadena ya no sería necesariamente óptima en ese caso.

Utilizo la biblioteca LEMON por su flujo de costo mínimo y algoritmos de circuito euleriano. (Esta fue la primera vez que usé esta biblioteca, y me impresionó, definitivamente la volveré a usar para futuras necesidades de algoritmos de gráficos). LEMON viene con cuatro algoritmos de flujo de costo mínimo diferentes; se puede tratar aquí con --net, --cost, --cap, y --cycle(por defecto).

El programa se ejecuta en 0,5 segundos , produciendo esta cadena de salida .

#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;
}
Anders Kaseorg
fuente
Si bien no puedo afirmar que entiendo cómo funciona el flujo mínimo, si esto es realmente óptimo, ¡bien hecho!
Nathan Merrill
1
Lamento decepcionar: PI no había pensado en una red de flujo; Eso es muy elegante. Me tomó un tiempo entender cómo unía las palabras en su red antes de que finalmente me diera cuenta de "Por cada prefijo o sufijo (estricto) de cualquier palabra, dibuje un nodo a" significa que los nodos "a" se pueden compartir entre Múltiples palabras.
Dave
1
¡Además, su solución es aproximadamente 2.000 veces más rápida que la mía!
Dave
1
¿Quizás ayudar a este tipo ( cs.cmu.edu/~tom7/portmantout ) con su intento de algo similar?
Oliver Daugherty-Long
2
@ OliverDaugherty-Largo ¡ Listo ! ( Realmente esta vez.) Los límites más conocidos anteriormente fueron 520732 ≤ longitud óptima ≤ 537136, y creo que he mejorado ambos límites a 536186.
Anders Kaseorg
13

Java 8, ~ 5 minutos, duración de 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);
        }
    }

}

Entrada:

  • un archivo llamado 'words.txt' en el directorio de trabajo, exactamente en el mismo formato que el archivo en la publicación principal

Salida:

Finished I/O
Removed direct containers
Finished algorithm
The string: 
[Moved to pastebin](http://pastebin.com/iygyR3zL)
Length: 
39279
Verified: 
true
Fénix Socrático
fuente
2
FGITW, y en Java no menos. Usted señor tiene mi voto.
Patrick Roberts el
2
¡Agradable! Te deshiciste de los 26,609personajes.
R. Kap
@ R.Kap ir figura! Ni siquiera creo que para calcular ... Debe haber un algoritmo inteligente, sin embargo, esto es sólo el primero que me vino a la mente ...
socrático Phoenix
7

Python 2, 39254 caracteres

Tarda 1-2 minutos en ejecutarse en mi máquina, funciona tomando la palabra más larga y luego siempre agregando la palabra a la cadena de resultados que tiene la mayoría de las cadenas en común. (Antes de eso, todas las palabras que son subcadenas de otras palabras se eliminan para evitar adiciones innecesarias a la cadena).

Actualización: Intenté mirar en ambas direcciones, pero eso no mejora. (¿Tal vez está usando palabras que se pueden usar mejor más adelante?)

Enlace a la palabra en pastebin.

primeros 100 caracteres:

telecommunicationsawayneillegallynnbabyenbcjrxltdxmlbsrcwvtxxxboxespnycdsriconsentencessexyrsslipodc

Código:

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())
KarlKastor
fuente
2
Welp, he sido derrotado por 25 personajes ... +1 por eso
Socratic Phoenix
¡Buen trabajo! Tuve una idea similar pero ya tenías una respuesta. Mi versión comienza con una palabra pequeña en lugar de una palabra grande, además de algunas otras podas que hacen que pierda drásticamente el factor tiempo, lo que lleva hasta 3 veces la cantidad de tiempo de ejecución.
Value Ink el
5

Ruby, 39222 caracteres

Utiliza un enfoque similar a @KarlKastor en su respuesta de Python, pero la cadena de inicio es una de las palabras más pequeñas en lugar de la más grande. Otra optimización (no sé cuánto ayuda) es que entre cada adición, elimina cualquier palabra que ya haya sido incluida en la cadena debido a la superposición de palabras.

Se ejecuta en poco más de 4 minutos en mi máquina, sin contar la solicitud web para recuperar la lista de palabras, pero no del todo 4:20.

La palabra sobre 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"
Tinta de valor
fuente
3

PowerShell v2 +, 46152 caracteres

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

Toma la entrada como una lista, la convierte en una ArrayList (para que podamos manipularla). Nosotros sortpor lengthen -desorden cending. Entonces, whiletodavía tenemos palabras en nuestra matriz de entrada, haga un ciclo. En cada iteración, configure Helper $xpara que sea igual a cuántos nos quedan, agregue el siguiente elemento de la lista a nuestra salida $oy luego rastree todo lo que todavía está en nuestra lista. Si el .IndexOfno es igual a -1(es decir, la palabra se encontró en algún lugar $o), eliminamos esa palabra de nuestra lista de palabras restantes. Finalmente, al final, salida $o.

No tengo acceso a un Pastebin o similar, así que aquí está el principio y el final de la palabra para temporal - telecommunicationscharacterizationresponsibilitiessublimedirectory...fcmxvtwvfxwujmjsuhjjrxjdbkdxqc. Lo que supongo que ha reducido unos 20,000 caracteres de la entrada, así que supongo que no está tan mal.

Estoy trabajando en mejoras.

AdmBorkBork
fuente
0

PHP 46612 caracteres

Esto es solo un comienzo. Espero mejorarlo. Todo lo que he hecho hasta ahora es eliminar cualquier palabra que sea una subcadena de otra palabra. Estoy trabajando en 3 copias de la matriz, pero la memoria no parece ser 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);
TecBrat
fuente