복합 영어


28

복합 단어는 둘 이상의 단어를 포함하는 단어입니다. 그러나 우리는 그보다 더 잘할 수 있습니다. 모든 단어 를 포함하는 1 개의 (무의미한) 단어만들어야 합니다.

그러나 우리는이 단어가 가능한 짧게되기를 원합니다. 이를 위해 겹치는 글자를 사용할 수 있습니다.

예를 들어 단어 목록이 ["cat", "atom", "a"]이면을 반환하려고합니다 "catom".

입출력

프로그램은 단어 목록을 입력으로 취하고 복합 단어를 출력으로 리턴해야합니다.

Google에 따르면 사용할 단어 목록 은 영어로 된 최상위 10000 단어 입니다 (이 목록이 너무 쉬운 것으로 판명되면 더 긴 단어 로 변경할 수 있습니다). 참고로 간단히 각 단어를 추가하면 65888 점을 얻습니다.

점수는 최종 단어의 글자 수이며 낮을수록 좋습니다. 타이 브레이커는 첫 포스터에갑니다.



1
@Loovjo 아니오, 그러나 그 bruteforcing이 실행하기에 충분히 빠르면 단어 목록을 변경하여 더 길게 만듭니다.
Nathan Merrill

1
@PatrickRoberts 귀하의 답변에 맞는 경우, props :) pastebin / gist 링크는 훌륭하지만 필수는 아닙니다.
Nathan Merrill

1
흠, 좋은 비대칭 여행 세일즈맨 휴리스틱을 아는 사람은 누구입니까?
Dave

2
줄 바꿈이 없으며 결정 론적입니다.
Nathan Merrill

답변:


26

C ++, 최종 단어 길이 : 38272

(최적화 된 버전은 약 20 분이 걸렸습니다)

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

검증 bash one-liner :

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

또한 꽤 멋진 진행 단어를 만들어 냈습니다. 여기 내가 좋아하는 것들이 있습니다 :

  • 폴리 에스터 데이 (합성 노스탤지어)
  • 아프가니스탄 불 (당신이 싫어하는 정치인 삽입
  • togethernet (친숙한 인터넷)
  • elephantom (큰 유령)
  • thundergroundwaterproof ( "그들이 그것을 thundergroundwaterproof을 할 필요성을 느낀 이유는 모르겠지만, 그것은 나를 긴장 만들고있다"에서와 같이)

과:

  • codescribingo (이 사이트에서 다가오는 도전이 아닐까요?)

최종 출력은 pastebin에 있습니다 : http://pastebin.com/j3qYb65b


2
최적의 솔루션을 찾고자하는 다른 사람들에게 유용 할 수있는 관찰 : 문제는 비 유클리드 비대칭 이동 판매원 문제로 축소 될 수 있습니다. 요소 i, j = max_word_length - overlap(word[i], word[j])(여기서 overlap오른쪽에서 겹침을 검사하는 위치) 두 번째 왼쪽의 첫 번째 인수). 이것을 해결 (행운!) 한 다음 결과 루프를 가장 높은 비용 (가장 겹치지 않음)으로 자르면 최적의 솔루션을 제공하기 위해 병합 될 수있는 정렬 된 단어 목록이 제공됩니다.
Dave

인상적입니다. 이것은 현재 목록의 모든 단어를 매번 다른 단어와 비교하고 있습니까? 나는 이것을 고려하고 있었지만 합리적인 시간에 실행되도록 무작위 샘플을 확인해야한다고 가정했다.
trichoplax 1

1
@ValueInk 예 캐싱은 성능을 크게 향상시킵니다. 이전 버전에는 그 기능이 많았지 만 많은 복잡성이 추가되었으므로 일부 논리를 적용하면 로트를 다시 작성해야했습니다. 대신 캐싱을 삭제하기로 결정했습니다. 또한 아니오, 이것은 완전히 최적이 아닙니다. 욕심 많은 알고리즘이므로 트레이드 오프를 판단 할 수 없으며 "똑같이 좋은"옵션 중에서 선택할 수 없습니다. (NP-Hard) 최적 솔루션에 대한 내 TSP 의견을 참조하십시오.
Dave

1
@ trichoplax yup, 그것이하는 일입니다. 실행 시간은 O (n ^ 3)이며이 샘플 크기에는 그리 나쁘지 않습니다. 캐싱을 사용하면 O (n ^ 2)로 줄일 수 있습니다. 내부 검사는 병렬 처리가 가능하므로 더 큰 샘플의 경우에도 스레딩 / 분산 계산으로 적절한 시간에 실행될 수 있습니다. 또한 이것은 각 단계에서 가능한 오버랩 폭의 범위를 아는 것으로부터 속도를 크게 향상 시켜서 런타임을 10 배나 줄였습니다.
Dave

2
우리는 모든 a에 대해 오버랩 (a, b) ≥ min {overlap (a, d), 오버랩 (c, d), 오버랩 (c, b)}라는 재미있는 제약 조건을 가지고 있기 때문에 일반적인 TSP만큼 어렵지 않을 수 있습니다 , b, c, d.
Anders Kaseorg

21

C ++ 11, 38272 자, 최적으로 입증 됨

이 알고리즘은 솔루션에 대한 하한을 제공합니다. 이 경우, 하한을 달성하고 최적의 38272 문자 솔루션을 출력 할 수 있습니다. (이것은 Dave의 탐욕스러운 알고리즘이 찾은 솔루션과 일치합니다. 나는 그것이 최적이라는 것을 발견 한 것에 놀랐고 조금 실망했습니다. 그러나 우리는 있습니다.)

다음과 같이 구축 된 네트워크 에서 최소 비용 흐름 문제를 해결하여 작동 합니다.

  • 첫째, 다른 단어에 포함 된 단어는 중복됩니다. 버립니다.
  • 모든 단어 w 에 대해 두 개의 노드 w _0과 w _1을 그 립니다 . 여기서 w _0은 용량 1의 소스이고 w _1은 용량 1의 싱크입니다.
  • 단어의 모든 (엄격한) 접두사 또는 접미사 a 에 대해 노드 a를 그 립니다 .
  • 모든 접미사 들어 의 w 에서 아크 그릴 w 에 _0을 용량 1 0 비용.
  • w의 모든 접두사 a 에 대해 용량 1 및 비용 길이 ( w )-길이 ( a ) 를 사용 하여 a 에서 w _1 까지 호를 립니다 .

길이의 모든 문자열 N 모든 단어를 포함 최대 비용이 네트워크의 흐름으로 변환 할 수 없음 . 따라서이 네트워크의 최소 비용 흐름은 해당 문자열의 최단 길이에 대한 하한입니다.

운이 좋으면 (이 경우에는) , w _0에서 w _1으로 들어오는 흐름을 리디렉션 한 후 연결된 구성 요소가 하나만 있고 비어있는 노드를 통과하는 최적의 흐름을 찾습니다. 끈. 그렇다면 그곳에서 시작하여 끝나는 Eulerian 회로가 포함됩니다. 이러한 Eulerian 회로는 최적 길이의 문자열로 다시 읽을 수 있습니다.

운이 좋지 않은 경우, Eulerian 회로가 존재하도록하기 위해 비어있는 스트링과 연결된 다른 컴포넌트의 가장 짧은 스트링 사이에 약간의 아크를 추가하십시오. 이 경우 문자열이 더 이상 최적 일 필요는 없습니다.

최소 비용 흐름 및 Eulerian 회로 알고리즘에 LEMON 라이브러리를 사용합니다 . (이 라이브러리를 사용한 것은 이번이 처음이었고 감동했습니다. 향후 그래프 알고리즘 요구에 다시 사용할 것입니다.) LEMON에는 네 가지 최소 비용 흐름 알고리즘이 있습니다. 당신이 여기를 시도 할 수 있습니다와 --net, --cost, --cap, 및--cycle (기본값).

프로그램은 0.5 초 안에 실행 되며이 출력 문자열을 생성 합니다 .

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

최소 흐름이 어떻게 작동하는지 이해한다고 주장 할 수는 없지만 이것이 실제로 최적이라면 잘 완료되었습니다!
Nathan Merrill

1
실망 스럽습니다 : PI는 흐름 네트워크를 생각하지 않았습니다. 꽤 우아합니다. "어떤 단어의 모든 접두사 또는 접미사 a에 대해 노드 a를 그립니다"라는 단어를 마지막으로 깨 달기 전에 네트워크에서 단어를 어떻게 연결했는지 이해하는 데 시간이 걸렸습니다. 여러 단어.
Dave

1
또한 귀하의 솔루션은 내 것보다 약 2,000 배 빠릅니다!
Dave

1
어쩌면이 사람 ( cs.cmu.edu/~tom7/portmantout )이 비슷한 일을 시도 하도록 도울 수 있습니까?
Oliver Daugherty-Long

2
@ OliverDaugherty-Long Done ! (이번 실제) 가장 잘 알려진 경계 는 520732 ≤ 최적 길이 ≤ 537136이며 두 경계를 모두 536186으로 개선했다고 생각합니다.
Anders Kaseorg

13

Java 8, ~ 5 분, 길이 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);
        }
    }

}

입력:

  • 작업 디렉토리의 'words.txt'라는 파일, 기본 게시물의 파일과 정확히 동일한 형식

산출:

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

2
FGITW, 그리고 자바에서. 당신은 내 투표를했습니다.
Patrick Roberts

2
좋은! 당신은 26,609문자를 제거했습니다 .
R. Kap

@ R.Kap go figure! 나는 심지어 그것을 계산하려고 생각하지 않았다 ...하지만 더 똑똑한 알고리즘이 있어야합니다, 이것은 생각 나는 첫 번째 것입니다 ...
Socratic Phoenix

7

파이썬 2, 39254 자

내 컴퓨터에서 실행하는 데 1-2 분이 걸리고 가장 긴 단어를 가져 와서 가장 일반적인 문자열을 가진 결과 문자열에 항상 단어를 추가하여 작동합니다. (다른 단어의 하위 문자열 인 모든 단어는 문자열에 불필요한 추가를 방지하기 위해 제거되기 전에)

업데이트 : 양방향으로 보려고했지만 더 나아지지는 않습니다. (나중에 더 잘 사용할 수있는 단어를 사용하고 있습니까?)

pastebin의 단어에 연결하십시오.

처음 100 자 :

telecommunicationsawayneillegallynnbabyenbcjrxltdxmlbsrcwvtxxxboxespnycdsriconsentencessexyrsslipodc

암호:

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
웰프, 나는 25 문자로 구타되었습니다 ... +1
Socratic Phoenix

잘 하셨어요! 나는 비슷한 생각을 가지고 있지만 이미 대답했다. 내 버전은 큰 단어 대신 작은 단어와 함께 시작 시간을 최대 3 배까지 걸리는 시간 요소를 크게 잃어 버리는 다른 가지 치기를 시작합니다.
가치 잉크

5

루비, 39222 자

파이썬 답변에서 @KarlKastor와 비슷한 접근 방식을 사용하지만 시작 문자열은 가장 큰 단어가 아닌 가장 작은 단어 중 하나입니다. 또 다른 최적화 (어떻게 도움이되는지 모르겠습니다)는 각 추가 사이에 겹치는 단어로 인해 문자열에 이미 포함되었을 수있는 단어를 정리합니다.

내 컴퓨터에서 4 분이 조금 지나서 실행되며 단어 목록을 검색하기위한 웹 요청은 포함하지 않지만 4:20은 아닙니다.

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 자

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

입력을리스트로 받아서 ArrayList로 캐스트합니다 (따라서 조작 할 수 있습니다). 우리 sort로 그것을 length에서 -des순서를 cending. 그런 다음 while여전히 입력 배열에 단어가 있고 루프를 수행합니다. 반복 $x할 때 마다 도우미 가 남은 수와 같도록 설정 하고 목록의 다음 항목을 출력 $o에 고정한 다음 목록에있는 모든 항목 을 탐색합니다. (가) 경우 .IndexOf와 동일하지 않은 -1(즉, 단어가있는 곳을 발견했다 $o), 우리는 남아있는 단어의 우리의 목록에서 해당 단어를 제거합니다. 마지막으로 output $o.

Pastebin 또는 이와 유사한 기능에 액세스 할 수 없으므로 다음은 임시 단어의 시작과 끝입니다. telecommunicationscharacterizationresponsibilitiessublimedirectory...fcmxvtwvfxwujmjsuhjjrxjdbkdxqc . 나는 입력에서 약 20,000 문자를 깎아서 그렇게 나쁘지 않다고 생각합니다.

개선 작업 중입니다.


0

PHP 46612 자

이것은 시작에 불과합니다. 나는 그것을 개선하기를 희망합니다. 지금까지 한 일은 다른 단어의 하위 문자열 인 단어를 제거하는 것입니다. 어레이의 3 복사본을 작업 중이지만 메모리에 문제가없는 것 같습니다.

<?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);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.