Anglais composé

28

Un mot composé est un mot qui contient 2 mots ou plus. Mais nous pouvons faire mieux que cela. Nous avons besoin de vous pour créer 1 mot (absurde) qui contient chaque mot .

Cependant, nous voulons que ce mot soit aussi court que possible. Nous pouvons utiliser des lettres qui se chevauchent pour y parvenir.

Par exemple, si votre liste de mots était ["cat", "atom", "a"], vous voudriez revenir "catom".

Entrée sortie

Votre programme devra prendre une liste de mots en entrée et renvoyer un mot composé en sortie.

La liste de mots que vous utiliserez est les 10000 premiers mots en anglais, selon Google (si cette liste s'avère trop facile, je peux la changer en une plus longue). Pour référence, le simple fait d'ajouter chaque mot vous donne un score de 65888.

Votre score est le nombre de lettres de votre dernier mot, plus c'est bas, mieux c'est. Le bris d'égalité va à la première affiche.

Nathan Merrill
la source
1
@Loovjo non, mais s'il s'avère que le bruteforcing est assez rapide pour s'exécuter, alors je changerai la liste de mots pour l'allonger.
Nathan Merrill
1
@PatrickRoberts Si cela correspond à votre réponse, des accessoires pour vous :) Un lien pastebin / gist serait bien, mais n'est pas requis.
Nathan Merrill
1
Hmm, qui connaît une bonne heuristique asymétrique de vendeur itinérant?
Dave
2
Pas d'emballage, et oui à déterministe.
Nathan Merrill

Réponses:

26

C ++, longueur du dernier mot: 38272

(la version optimisée a pris environ 20 minutes)

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

Vérification bash one-liner:

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

Il a également produit des mots en cours assez cool. Voici quelques-uns de mes favoris:

  • polyesterday (nostalgie synthétique)
  • afghanistanbul (quelque chose [insérer un politicien que vous n'aimez pas] dirait)
  • togethernet (l'internet convivial)
  • éléphantom (un grand fantôme)
  • thundergroundwaterproof (comme dans "Je ne sais pas pourquoi ils ont ressenti le besoin de le rendre thundergroundwater, mais ça me rend nerveux")

Et:

  • codescribingo (peut-être un prochain défi sur ce site?)

La sortie finale est sur pastebin ici: http://pastebin.com/j3qYb65b

Dave
la source
2
Une observation qui pourrait être utile à ceux qui cherchent à obtenir la solution optimale: le problème peut être réduit à un problème de voyageur ambulant non euclidien et asymétrique: définir une matrice où l'élément i, j = max_word_length - overlap(word[i], word[j])(où overlapvérifie le chevauchement à droite de la premier argument à gauche du second). Résoudre cela (bonne chance!) Puis couper la boucle résultante au coût le plus élevé (chevauchement le plus faible) donnera une liste ordonnée de mots qui peuvent être fusionnés pour donner une solution optimale.
Dave
Impressionnant. Est-ce que cela vérifie chaque mot de la liste actuelle les uns par rapport aux autres, à chaque fois? J'envisageais cela, mais j'ai supposé que je devais simplement vérifier un échantillon aléatoire pour le faire fonctionner dans un délai raisonnable.
trichoplax
1
@ValueInk yes la mise en cache serait un gros coup de pouce pour les performances. Une version antérieure avait cela, mais cela ajoute beaucoup de complexité, donc quand j'ai adapté une logique, j'ai dû réécrire le lot. Au lieu de cela, j'ai choisi de supprimer la mise en cache. Non, ce n'est pas complètement optimal. C'est un algorithme gourmand donc il ne peut pas juger des compromis, et il est incapable de choisir entre des options "tout aussi bonnes". Voir mon commentaire TSP pour la solution optimale (NP-Hard).
Dave
1
@trichoplax yup, c'est ce qu'il fait. Le temps d'exécution est O (n ^ 3), ce qui n'est pas si mal pour cette taille d'échantillon. Avec la mise en cache, elle pourrait être réduite à O (n ^ 2). La vérification interne est également très parallèle, donc même pour des échantillons plus grands, elle peut s'exécuter dans un délai raisonnable avec le calcul de threads / distribué. De plus, cela permet d'augmenter considérablement la vitesse en connaissant la plage de largeurs de chevauchement possibles pour chaque étape, ce qui a réduit le temps d'exécution d'un facteur 10.
Dave
2
Ce n'est peut-être pas aussi difficile que le TSP général, car nous avons la drôle de contrainte qui se chevauchent (a, b) ≥ min {chevauchement (a, d), chevauchement (c, d), chevauchement (c, b)} pour tout a , b, c, d.
Anders Kaseorg
21

C ++ 11, 38272 lettres, optimales prouvées

Cet algorithme est garanti pour fournir une limite inférieure sur la solution. Dans ce cas, il est capable d'atteindre la limite inférieure et de produire une solution optimale de 38272 lettres. (Cela correspond à la solution trouvée par l'algorithme gourmand de Dave. J'ai été surpris et un peu déçu de découvrir que c'est optimal, mais nous y voilà.)

Il fonctionne en résolvant le problème de flux à coût minimum sur le réseau construit comme suit.

  • Premièrement, tous les mots contenus dans d'autres mots sont redondants; jetez-les.
  • Pour chaque mot w , dessinez deux nœuds w _0 et w _1, où w _0 est une source avec une capacité 1 et w _1 est un puits avec une capacité 1.
  • Pour chaque préfixe ou suffixe (strict) a de n'importe quel mot, dessinez un nœud a .
  • Pour chaque suffixe a de w , tracez un arc de w _0 à a de capacité 1 et coûte 0.
  • Pour chaque préfixe a de w , tracez un arc de a à w _1 avec la capacité 1 et le coût longueur ( w ) - longueur ( a ).

Toute chaîne de longueur n qui contient chaque mot peut être convertie en un flux sur ce réseau avec un coût au plus n . Par conséquent, le flux de coûts minimum sur ce réseau est une limite inférieure sur la longueur de la chaîne la plus courte.

Si nous sommes chanceux - et dans ce cas, nous le sommes - alors après avoir redirigé le flux entrant dans w _1 de retour de w _0, nous trouverons un flux optimal qui n'a qu'un seul composant connecté et qui traverse le nœud pour le vide chaîne. Si c'est le cas, il contiendra un circuit eulérien commençant et se terminant là. Un tel circuit eulérien peut être lu comme une chaîne de longueur optimale.

Si nous n'avons pas eu de chance, ajoutez des arcs supplémentaires entre la chaîne vide et les chaînes les plus courtes des autres composants connectés afin de garantir l'existence d'un circuit eulérien. La chaîne ne serait plus nécessairement optimale dans ce cas.

J'utilise la bibliothèque LEMON pour ses algorithmes de flux à moindre coût et de circuit eulérien. (C'était la première fois que j'utilisais cette bibliothèque, et j'ai été impressionné — je vais certainement l'utiliser à nouveau pour les futurs besoins d'algorithmes de graphes.) LEMON est livré avec quatre algorithmes de flux à coût minimum différents; vous pouvez les essayer ici avec --net, --cost, --capet --cycle(par défaut).

Le programme s'exécute en 0,5 seconde , produisant cette chaîne de sortie .

#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
la source
Bien que je ne puisse pas prétendre comprendre comment fonctionne le débit minimum, si c'est vraiment optimal, bien joué!
Nathan Merrill
1
Désolé de décevoir: PI n'avait pas pensé à un réseau de flux; c'est assez élégant. Il m'a fallu un certain temps pour comprendre comment vous reliez les mots ensemble dans votre réseau avant de finalement réaliser "Pour chaque préfixe (ou suffixe) strict d'un mot, dessinez un nœud a" signifiait que les nœuds "a" pouvaient être partagés entre plusieurs mots.
Dave
1
De plus, votre solution est environ 2 000 fois plus rapide que la mienne!
Dave
1
Peut-être aider ce mec ( cs.cmu.edu/~tom7/portmantout ) avec sa tentative de quelque chose de similaire?
Oliver Daugherty-Long
2
@ OliverDaugherty-Long fait ! (Pour de vrai cette fois.) Les meilleures limites précédemment connues étaient 520732 ≤ longueur optimale ≤ 537136, et je crois que j'ai amélioré les deux limites à 536186.
Anders Kaseorg
13

Java 8, ~ 5 minutes, durée 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);
        }
    }

}

Contribution:

  • un fichier appelé 'words.txt' dans le répertoire de travail, dans le même format exact que le fichier dans le message principal

Sortie:

Finished I/O
Removed direct containers
Finished algorithm
The string: 
[Moved to pastebin](http://pastebin.com/iygyR3zL)
Length: 
39279
Verified: 
true
Phénix socratique
la source
2
FGITW, et en Java pas moins. Vous avez mon vote monsieur.
Patrick Roberts
2
Agréable! Vous vous êtes débarrassé des 26,609personnages.
R. Kap
@ R.Kap go figure! Je n'ai même pas pensé à calculer ça ... Il doit y avoir un algorithme plus intelligent cependant, c'est juste la première chose qui m'est venue à l'esprit ...
Socratic Phoenix
7

Python 2, 39254 caractères

Prend 1-2 minutes pour fonctionner sur ma machine, fonctionne en prenant le mot le plus long puis en ajoutant toujours le mot à la chaîne de résultat qui a le plus de chaînes en commun. (Avant cela, tous les mots qui sont des sous-chaînes d'autres mots sont supprimés pour éviter des ajouts inutiles à la chaîne.)

Mise à jour: J'ai essayé de regarder dans les deux sens, mais cela ne fait pas mieux. (peut-être utilise-t-il des mots qui peuvent être mieux utilisés plus tard?)

Lien vers le mot sur pastebin.

100 premiers caractères:

telecommunicationsawayneillegallynnbabyenbcjrxltdxmlbsrcwvtxxxboxespnycdsriconsentencessexyrsslipodc

Code:

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
la source
2
Welp, j'ai été battu par 25 personnages ... +1 pour ça
Socratic Phoenix
Bon travail! J'avais une idée similaire mais vous aviez déjà une réponse. Ma version commence par un petit mot au lieu d'un grand mot, plus quelques autres élagages qui lui font perdre considérablement le facteur temps, prenant jusqu'à 3 fois plus de temps à exécuter.
Value Ink
5

Ruby, 39222 caractères

Utilise une approche similaire à @KarlKastor dans sa réponse Python, mais la chaîne de départ est l'un des plus petits mots au lieu du plus grand. Une autre optimisation (je ne sais pas combien cela aide) est qu'entre chaque ajout, il élague tous les mots qui ont peut-être déjà été inclus dans la chaîne en raison de chevauchements de mots.

Fonctionne en un peu plus de 4 minutes sur ma machine, sans compter la requête Web pour récupérer la liste des mots, mais pas tout à fait 4:20.

Le mot sur 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"
Encre de valeur
la source
3

PowerShell v2 +, 46152 caractères

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

Prend l'entrée sous forme de liste, la transforme en ArrayList (afin que nous puissions la manipuler). Nous sortpar lengthdans l' -desordre cending. Ensuite, whilenous avons encore des mots dans notre tableau d'entrée, faites une boucle. À chaque itération, définissez helper $xcomme étant égal au nombre restant, clouez le prochain élément de la liste à notre sortie $o, puis parcourez tout ce qui se trouve encore dans notre liste. Si le .IndexOfn'est pas égal à -1(c'est- à -dire que le mot a été trouvé quelque part $o), nous supprimons ce mot de notre liste de mots restants. Enfin, à la fin, la sortie $o.

Je n'ai pas accès à un Pastebin ou similaire, alors voici le début et la fin du mot pour temporaire - telecommunicationscharacterizationresponsibilitiessublimedirectory...fcmxvtwvfxwujmjsuhjjrxjdbkdxqc. Ce qui, je suppose, a rasé environ 20 000 caractères de l'entrée, donc pas si mal, je suppose.

Je travaille sur des améliorations.

AdmBorkBork
la source
0

PHP 46612 caractères

Ceci est juste un début. J'espère l'améliorer. Tout ce que j'ai fait jusqu'à présent est de supprimer tout mot qui est une sous-chaîne d'un autre mot. Je travaille sur 3 copies du tableau, mais la mémoire ne semble pas être un problème.

<?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
la source