Compounding English

28

Słowo złożone to słowo, które zawiera 2 lub więcej słów. Możemy jednak zrobić to lepiej. Musimy stworzyć 1 (nonsensowne) słowo, które zawiera każde słowo .

Chcemy jednak, aby to słowo było jak najkrótsze. W tym celu możemy użyć nakładających się liter.

Na przykład, jeśli twoja lista słów była ["cat", "atom", "a"], chciałbyś powrócić "catom".

Wejście wyjście

Twój program będzie musiał pobrać listę słów jako dane wejściowe i zwrócić słowo złożone jako dane wyjściowe.

Według Google lista słów, której będziesz używać, to 10000 najpopularniejszych słów w języku angielskim (jeśli ta lista okaże się zbyt łatwa, mogę ją zmienić na dłuższą). Dla porównania, po prostu dołączenie każdego słowa daje wynik 65888.

Twój wynik to liczba liter w ostatnim słowie, im niższa, tym lepsza. Łamacz remisów trafia na pierwszy plakat.

Nathan Merrill
źródło
1
@Loovjo nie, ale jeśli okaże się, że brutowanie jest wystarczająco szybkie, aby uruchomić, zmienię listę słów, aby wydłużyć.
Nathan Merrill
1
@PatrickRoberts Jeśli pasuje do twojej odpowiedzi, podpiera cię :) Link do pastebin / gist byłby świetny, ale nie jest wymagany.
Nathan Merrill,
1
Hmm, kto wie, że heurystyczny dobry asymetryczny sprzedawca w podróży?
Dave
2
Bez zawijania i tak dla deterministycznego.
Nathan Merrill,

Odpowiedzi:

26

C ++, końcowa długość słowa: 38272

(zoptymalizowana wersja zajęła około 20 minut)

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

Weryfikacja bash one-liner:

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

Stworzyło też całkiem fajne słowa w toku. Oto niektóre z moich ulubionych:

  • dzień poliestru (syntetyczna nostalgia)
  • afganistan (coś [wstawiłoby się polityka, którego nie lubisz] powiedziałby)
  • togethernet (przyjazny internet)
  • słoń (duży duch)
  • wodoodporny (jak w „Nie wiem, dlaczego czuli potrzebę uczynienia go wodoodpornym, ale denerwuje mnie”)

I:

  • codecribingo (może nadchodzące wyzwanie na tej stronie?)

Ostateczne wyjście znajduje się na pastebin tutaj: http://pastebin.com/j3qYb65b

Dave
źródło
2
Spostrzeżenie, które może być przydatne dla innych, którzy szukają optymalnego rozwiązania: problem można sprowadzić do nie-euklidesowego, asymetrycznego problemu sprzedawcy podróżującego: zdefiniuj macierz, w której element i, j = max_word_length - overlap(word[i], word[j])(gdzie overlapsprawdza nakładanie się z prawej strony pierwszy argument po lewej stronie drugiego). Rozwiązanie tego (powodzenia!), A następnie odcięcie powstałej pętli przy najwyższym koszcie (najniższe nakładanie się) da uporządkowaną listę słów, które można połączyć, aby uzyskać optymalne rozwiązanie.
Dave
Imponujący. Czy to sprawdza, czy każde słowo na bieżącej liście ma się do siebie, za każdym razem? Zastanawiałem się nad tym, ale założyłem, że muszę po prostu sprawdzić losową próbkę, aby uruchomić ją w rozsądnym czasie.
trichoplax
1
@ValueInk tak buforowanie byłoby dużym wzrostem wydajności. Wcześniejsza wersja miała to, ale dodaje wiele komplikacji, więc kiedy dostosowałem logikę, musiałem przepisać dużo. Zamiast tego postanowiłem porzucić buforowanie. Również nie, nie jest to całkowicie optymalne. Jest to chciwy algorytm, więc nie może oceniać kompromisów i nie może wybierać między „równie dobrymi” opcjami. Zobacz mój komentarz do TSP dla optymalnego rozwiązania (NP-Hard).
Dave
1
@trichoplax tak, właśnie to robi. Czas działania wynosi O (n ^ 3), co nie jest takie złe dla tej wielkości próbki. Przy buforowaniu można go zredukować do O (n ^ 2). Kontrola wewnętrzna jest również bardzo równoległa, więc nawet w przypadku większych próbek może działać w rozsądnym czasie z wątkami / obliczeniami rozproszonymi. Uzyskuje się także duży wzrost prędkości dzięki znajomości zakresu możliwych szerokości nakładania się dla każdego kroku, co skraca czas działania 10 razy.
Dave
2
Może to nie być tak trudne jak ogólny TSP, ponieważ mamy zabawne ograniczenie, które nakładają się (a, b) ≥ min {nakładanie (a, d), nakładanie (c, d), nakładanie (c, b)} dla wszystkich a , b, c, d.
Anders Kaseorg
21

C ++ 11, 38272 liter, sprawdzone jako optymalne

Ten algorytm gwarantuje dolną granicę rozwiązania. W takim przypadku jest w stanie osiągnąć dolną granicę i wydać optymalne rozwiązanie 38272 liter. (To pasuje do rozwiązania znalezionego przez chciwy algorytm Dave'a. Byłem zaskoczony i trochę rozczarowany odkryciem, że jest optymalny, ale tak jest.)

Działa poprzez rozwiązanie problemu minimalnego kosztu przepływu w sieci zbudowanej w następujący sposób.

  • Po pierwsze, wszelkie słowa zawarte w innych słowach są zbędne; odrzucić je.
  • Dla każdego słowa w narysuj dwa węzły w _0 i w _1, gdzie w _0 to źródło o pojemności 1, a w _1 to ujście o pojemności 1.
  • Dla każdego (ścisłego) przedrostka lub przyrostka a dowolnego słowa narysuj węzeł a .
  • Dla każdego przyrostkiem A w W , rysowania łukowego w _0 do o pojemności 1 i koszt 0.
  • Dla każdego prefiksu a z W , rysowania łukowego do wagowo _1 o pojemności 1 i długości kosztów ( wagowo ) - długość ( a ).

Każdy ciąg długości n, który zawiera każde słowo, można przekształcić w przepływ w tej sieci, kosztując najwyżej n . Dlatego minimalny przepływ kosztów w tej sieci stanowi dolną granicę długości najkrótszego takiego ciągu.

Jeśli mamy szczęście - i w tym przypadku mamy - to po przekierowaniu przepływu wchodzącego do w _1 z powrotem z w _0, znajdziemy optymalny przepływ, który ma tylko jeden połączony komponent i który przechodzi przez węzeł dla pustego strunowy. Jeśli tak, będzie zawierać obwód Eulera, który zaczyna się i kończy. Taki obwód Eulera można odczytać jako ciąg optymalnej długości.

Jeśli nie będziemy mieli szczęścia, dodaj dodatkowe łuki między pustym łańcuchem a najkrótszymi łańcuchami w innych połączonych komponentach, aby upewnić się, że istnieje obwód Eulera. W takim przypadku łańcuch niekoniecznie byłby już optymalny.

Korzystam z biblioteki LEMON dla jej minimalnego kosztu przepływu i algorytmów obwodu Eulera. (To był mój pierwszy raz, kiedy korzystałem z tej biblioteki i byłem pod wrażeniem - na pewno użyję jej ponownie do przyszłych potrzeb algorytmów graficznych.) LEMON jest wyposażony w cztery różne algorytmy przepływu minimalnych kosztów; można spróbować je tutaj z --net, --cost, --capi --cycle(domyślnie).

Program działa w ciągu 0,5 sekundy , generując ten ciąg wyjściowy .

#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
źródło
Chociaż nie mogę twierdzić, że rozumiem, jak działa minimalny przepływ, jeśli to naprawdę jest optymalne, dobrze zrobione!
Nathan Merrill,
1
Przepraszam za rozczarowanie: PI nie myślał o sieci przepływowej; to całkiem eleganckie. Zajęło mi trochę czasu, aby zrozumieć, w jaki sposób łączysz słowa w swojej sieci, zanim w końcu zdałem sobie sprawę: „Dla każdego (ścisłego) prefiksu lub sufiksu a dowolnego słowa narysuj węzeł„ oznacza, że ​​węzły „a” mogą być dzielone między wiele słów.
Dave
1
Twoje rozwiązanie jest około 2000 razy szybsze niż moje!
Dave
1
Może pomożesz temu facetowi ( cs.cmu.edu/~tom7/portmantout ) w jego próbie podobnej rzeczy?
Oliver Daugherty-Long,
2
@ OliverDaugherty-Long Done ! (Tym razem naprawdę.) Najbardziej znanymi wcześniej granicami były 520732 ≤ optymalna długość ≤ 537136 i uważam, że poprawiłem obie granice do 536186.
Anders Kaseorg
13

Java 8, ~ 5 minut, długość 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);
        }
    }

}

Wkład:

  • plik o nazwie „words.txt” w katalogu roboczym, w dokładnie takim samym formacie, jak plik w głównym poście

Wydajność:

Finished I/O
Removed direct containers
Finished algorithm
The string: 
[Moved to pastebin](http://pastebin.com/iygyR3zL)
Length: 
39279
Verified: 
true
Socratic Phoenix
źródło
2
FGITW i nie mniej niż Java. Masz mój głos.
Patrick Roberts,
2
Miły! Pozbyłeś się 26,609postaci.
R. Kap
@ R.Kap idź rysunek! Nawet nie pomyślałem, żeby to obliczyć ... Musi być jednak mądrzejszy algorytm, to tylko pierwsza rzecz, jaka przyszła mi do głowy ...
Socratic Phoenix
7

Python 2, 39254 znaków

Uruchomienie na mojej maszynie zajmuje 1-2 minuty. Działa, biorąc najdłuższe słowo, a następnie zawsze dodając słowo do ciągu wynikowego, który ma najwięcej wspólnych ciągów. (Wcześniej wszystkie słowa będące podciągami innych słów są usuwane, aby zapobiec niepotrzebnemu dodawaniu do łańcucha).

Aktualizacja: Próbowałem spojrzeć w obu kierunkach, ale to nie robi nic lepszego. (może używa słów, których później można lepiej użyć?)

Link do słowa na pastebin.

pierwszych 100 znaków:

telecommunicationsawayneillegallynnbabyenbcjrxltdxmlbsrcwvtxxxboxespnycdsriconsentencessexyrsslipodc

Kod:

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
źródło
2
Welp, zostałem pobity przez 25 postaci ... +1 za to
Socratic Phoenix
Dobra robota! Miałem podobny pomysł, ale już miałeś odpowiedź. Moja wersja zaczyna się od małego słowa zamiast dużego słowa, a także od innych przycinania, które powoduje, że drastycznie traci czas, zajmując do 3 razy więcej czasu.
Wartość tuszu
5

Ruby, 39222 znaków

Używa podobnego podejścia do @KarlKastor w swojej odpowiedzi w Pythonie, ale łańcuch początkowy jest jednym z najmniejszych słów zamiast największego. Kolejną optymalizacją (nie wiem, jak bardzo to pomaga) jest to, że pomiędzy każdym dodaniem przycina wszystkie słowa, które mogły być już zawarte w ciągu z powodu nakładających się słów.

Działa na mojej maszynie w nieco ponad 4 minuty, nie licząc żądania internetowego, aby pobrać listę słów, ale nie całkiem 4:20.

Słowo o 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"
Wartość tuszu
źródło
3

PowerShell v2 +, 46152 znaków

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

Pobiera dane wejściowe jako listę, rzutuje je na ArrayList (abyśmy mogli nimi manipulować). Mamy sortto lengthw -desporządku rosnącym. Następnie whilenadal mamy słowa w naszej tablicy wejściowej, wykonaj pętlę. W każdej iteracji ustaw pomocnika $xna równi z tym, ile nam pozostało, przypnij następny element na liście do naszej produkcji $o, a następnie przejrzyj wszystko, co wciąż jest na naszej liście. Jeśli .IndexOfto nie jest równe -1(tzn. Słowo zostało znalezione gdzieś w środku $o), usuwamy to słowo z naszej listy pozostałych słów. Wreszcie na koniec wyjście $o.

Nie mam dostępu do Pastebin lub podobnego, więc tutaj jest początek i koniec słowa tymczasowego - telecommunicationscharacterizationresponsibilitiessublimedirectory...fcmxvtwvfxwujmjsuhjjrxjdbkdxqc. Zgaduję, że zgoliłem około 20 000 znaków z wejścia, więc nie jest tak źle, jak sądzę.

Pracuję nad udoskonaleniami.

AdmBorkBork
źródło
0

PHP 46612 znaków

To dopiero początek. Mam nadzieję to poprawić. Wszystko, co do tej pory zrobiłem, to usunięcie dowolnego słowa, które jest podłańcuchem innego słowa. Pracuję nad 3 kopiami tablicy, ale pamięć nie wydaje się być problemem.

<?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
źródło