ประนอมภาษาอังกฤษ


28

คำประสมคือคำที่มี 2 คำขึ้นไป เราสามารถทำได้ดีกว่านั้น เราต้องการให้คุณสร้าง 1 (ไร้สาระ) คำที่มีทุกคำ

อย่างไรก็ตามเราต้องการให้คำนี้สั้นที่สุด เราสามารถใช้ตัวอักษรที่ทับซ้อนกันเพื่อให้บรรลุสิ่งนี้

ตัวอย่างเช่นถ้ารายการคำของคุณคุณจะต้องการที่จะกลับ["cat", "atom", "a"]"catom"

Input / Output

โปรแกรมของคุณจะต้องใช้รายการคำเป็นอินพุตและส่งคืนคำประสมเป็นเอาท์พุท

รายการคำที่คุณจะใช้คือ10,000 คำบนสุดในภาษาอังกฤษตาม Google (หากรายการนี้กลายเป็นเรื่องง่ายเกินไปฉันอาจเปลี่ยนเป็นคำที่ยาวกว่า) สำหรับการอ้างอิงเพียงต่อท้ายแต่ละคำจะให้คะแนน 65888

คะแนนของคุณคือจำนวนตัวอักษรในคำสุดท้ายของคุณต่ำกว่าดีกว่า ไทเกอร์เบรกเกอร์จะไปที่โปสเตอร์แรก



1
@ Loovjo ไม่ แต่ถ้ามันจบลงด้วยการที่ bruteforcing นั้นเร็วพอที่จะวิ่งแล้วฉันจะเปลี่ยนรายการคำเพื่อให้ยาวขึ้น
Nathan Merrill

1
@PatrickRoberts ถ้ามันเหมาะกับคำตอบของคุณอุปกรณ์สำหรับคุณ :) ลิงค์ Pastebin / Gist นั้นยอดเยี่ยม แต่ก็ไม่จำเป็น
Nathan Merrill

1
อืมใครจะรู้ฮิวริสติกของพนักงานขายที่ไม่สมมาตรกันดี
เดฟ

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

การยืนยันทุบตีหนึ่งซับ:

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

มันยังผลิตคำที่กำลังดำเนินการอยู่ นี่คือรายการโปรดของฉัน:

  • Polyesterday (ความคิดถึงสังเคราะห์)
  • afghanistanbul (สิ่งที่ [ใส่นักการเมืองที่คุณไม่ชอบ] จะพูด)
  • togethernet (อินเทอร์เน็ตที่เป็นมิตร)
  • elephantom (ผีใหญ่)
  • thundergroundwaterproof (เช่นเดียวกับใน "ฉันไม่รู้ว่าทำไมพวกเขาถึงรู้สึกว่าจำเป็นต้องทำให้มันกันน้ำกันฝนฟ้าคะนอง แต่มันทำให้ฉันกังวล")

และ:

  • codescribingo (อาจเป็นความท้าทายในเว็บไซต์นี้หรือ

ผลลัพธ์สุดท้ายอยู่ที่ pastebin ที่นี่: http://pastebin.com/j3qYb65b


2
การสังเกตซึ่งอาจเป็นประโยชน์กับผู้อื่นที่มองหาวิธีการแก้ปัญหาที่ดีที่สุด: ปัญหาสามารถลดลงเป็นผู้ที่ไม่ใช่ยูคลิด, ปัญหาพนักงานขายที่ไม่สมมาตรเดินทาง: กำหนดเมทริกซ์โดยที่องค์ประกอบ i, j = max_word_length - overlap(word[i], word[j])(ที่overlapตรวจสอบการทับซ้อนจากด้านขวาของ อาร์กิวเมนต์แรกทางด้านซ้ายของวินาที) การแก้ปัญหานี้ (ขอให้โชคดี!) จากนั้นการตัดลูปผลลัพธ์ที่ค่าใช้จ่ายสูงสุด (การทับซ้อนต่ำสุด) จะให้รายการคำที่เรียงลำดับซึ่งสามารถผสานเพื่อให้ได้คำตอบที่ดีที่สุด
เดฟ

ประทับใจ นี่เป็นการตรวจสอบทุกคำในรายการปัจจุบันเทียบกับแต่ละอื่น ๆ ทุกครั้งที่ผ่าน? ฉันกำลังพิจารณาเรื่องนี้อยู่ แต่สันนิษฐานว่าฉันต้องการตรวจสอบตัวอย่างแบบสุ่มเพื่อให้ทำงานได้ในเวลาที่เหมาะสม
trichoplax

1
@ คุ้มค่าใช่การแคชจะเป็นการเพิ่มประสิทธิภาพอย่างมาก รุ่นก่อนหน้านั้นมี แต่มันเพิ่มความซับซ้อนมากดังนั้นเมื่อฉันปรับตรรกะบางอย่างฉันต้องเขียนใหม่มาก ฉันเลือกที่จะทำการแคชแทน ยังไม่มีสิ่งนี้ไม่เหมาะอย่างสมบูรณ์ มันเป็นอัลกอริทึมที่โลภมากดังนั้นจึงไม่สามารถตัดสินการเสียเปรียบและไม่สามารถเลือกระหว่างตัวเลือก "ดีเท่า ๆ กัน" ดูความคิดเห็น TSP ของฉันสำหรับการแก้ปัญหาที่ดีที่สุด (NP-Hard)
เดฟ

1
@trichoplax yup นั่นคือสิ่งที่มันทำ เวลาทำงานคือ O (n ^ 3) ซึ่งไม่เลวสำหรับขนาดตัวอย่างนี้ ด้วยการแคชจะสามารถลดลงถึง O (n ^ 2) การตรวจสอบภายในยังเป็นแบบขนานมาก isable ดังนั้นแม้สำหรับตัวอย่างขนาดใหญ่ก็สามารถทำงานได้ในเวลาที่เหมาะสมด้วยการคำนวณเธรด / กระจาย นอกจากนี้สิ่งนี้ยังได้รับการเร่งความเร็วขนาดใหญ่จากการทราบช่วงของความกว้างของการทับซ้อนที่เป็นไปได้สำหรับแต่ละขั้นตอนซึ่งตัดการใช้งานด้วยปัจจัย 10
เดฟ

2
สิ่งนี้อาจไม่ยากเหมือน TSP ทั่วไปเนื่องจากเรามีข้อ จำกัด ที่ตลกที่ทับซ้อนกัน (a, b) ≥นาที {เหลื่อมกัน (a, d), ทับซ้อน (c, d), ทับซ้อน (c, b)} สำหรับทุกคน , b, c, d
Anders Kaseorg

21

C ++ 11, 38272 ตัวอักษรพิสูจน์แล้วว่าเหมาะสมที่สุด

อัลกอริทึมนี้รับประกันว่าจะให้ขอบเขตที่ต่ำกว่าในการแก้ปัญหา ในกรณีนี้จะสามารถบรรลุขอบเขตที่ต่ำกว่าและส่งออกโซลูชั่นจดหมาย 38272 ที่ดีที่สุด (สิ่งนี้ตรงกับวิธีแก้ปัญหาที่พบโดยอัลกอริธึมที่โลภของ Dave ฉันรู้สึกประหลาดใจและผิดหวังเล็กน้อยที่ค้นพบว่ามันเป็นวิธีที่ดีที่สุด

มันทำงานได้โดยการแก้ปัญหาการไหลของต้นทุนต่ำสุดบนเครือข่ายที่สร้างขึ้นดังนี้

  • ข้อแรกคำใด ๆ ที่มีคำอื่น ๆ เป็นแบบซ้ำซ้อน ทิ้งพวกมัน
  • สำหรับทุกคำwวาดสองโหนดw _0 และw _1 โดยที่w _0 เป็นแหล่งที่มาพร้อมความจุ 1 และw _1 คือ sink ที่มีความจุ 1
  • สำหรับคำนำหน้า (เข้มงวด) ทุกคำหรือคำต่อท้ายaของคำใด ๆ ให้วาดโหนดa
  • สำหรับส่วนต่อท้ายaของwให้วาดส่วนโค้งจากw _0 ถึงaด้วยความจุ 1 และค่าใช้จ่าย 0
  • สำหรับทุกคำนำหน้าa of wให้วาดส่วนโค้งจากaถึงw _1 ด้วยความจุ 1 และความยาวต้นทุน ( w ) - ความยาว ( a )

สตริงใด ๆ ของความยาวnที่มีทุกคำที่สามารถแปลงเป็นกระแสในเครือข่ายนี้กับค่าใช้จ่ายที่มากที่สุดn ดังนั้นการไหลของต้นทุนขั้นต่ำในเครือข่ายนี้จึงมีขอบเขตที่ต่ำกว่าตามความยาวของสตริงที่สั้นที่สุด

หากเราโชคดี - และในกรณีนี้เราเป็น - หลังจากนั้นเราเปลี่ยนเส้นทางการไหลเข้าสู่w _1 กลับออกมาจากw _0 เราจะพบการไหลที่ดีที่สุดที่มีเพียงส่วนเชื่อมต่อเดียวและผ่านโหนดสำหรับช่องว่าง เชือก ถ้าเป็นเช่นนั้นจะมีวงจร Eulerian เริ่มต้นและสิ้นสุดที่นั่น วงจร Eulerian ดังกล่าวสามารถอ่านกลับเป็นสตริงที่มีความยาวเหมาะสมที่สุด

หากเราไม่โชคดีให้เพิ่มส่วนโค้งพิเศษระหว่างสตริงว่างและสตริงที่สั้นที่สุดในส่วนประกอบที่เชื่อมต่ออื่น ๆ เพื่อให้แน่ใจว่ามีวงจร Eulerian อยู่ สตริงจะไม่เหมาะสมที่สุดในกรณีนั้นอีกต่อไป

ฉันใช้ห้องสมุดLEMONสำหรับขั้นตอนการคิดต้นทุนขั้นต่ำและวงจร Eulerian (นี่เป็นครั้งแรกที่ฉันใช้ห้องสมุดนี้และฉันรู้สึกประทับใจ - ฉันจะใช้มันอีกครั้งสำหรับความต้องการอัลกอริทึมกราฟในอนาคต) 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;
}

ในขณะที่ฉันไม่สามารถเรียกร้องให้เข้าใจว่า min flow ทำงานอย่างไรถ้านี่เป็นสิ่งที่ดีที่สุดทำได้ดีมาก!
นาธานเมอร์ริล

1
ขอโทษที่ทำให้ผิดหวัง: PI ไม่ได้คิดถึงเครือข่ายการไหล นั่นสวยหรู เอาฉันสักครู่เพื่อทำความเข้าใจวิธีที่คุณเชื่อมโยงคำต่าง ๆ เข้าด้วยกันในเครือข่ายของคุณก่อนที่ฉันจะรู้ว่า "สำหรับคำนำหน้า (เข้มงวด) ทุกคำหรือคำต่อท้ายของคำใด ๆ ให้วาดโหนด" หมายความว่า หลายคำ
เดฟ

1
โซลูชันของคุณเร็วกว่าของฉันประมาณ 2,000 เท่า!
เดฟ

1
อาจช่วยผู้ชายคนนี้ ( cs.cmu.edu/~tom7/portmantout ) ด้วยความพยายามในสิ่งเดียวกัน
Oliver Daugherty ยาว

2
@ OliverDaugherty-Long เรียบร้อยแล้ว ! (สำหรับเวลานี้จริง ๆ ) ขอบเขตที่รู้จักกันดีที่สุดก่อนหน้านี้คือ 520732 length ความยาวที่เหมาะสม≤ 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);
        }
    }

}

การป้อนข้อมูล:

  • ไฟล์ชื่อ 'word.txt' ในไดเรกทอรีทำงานในรูปแบบเดียวกับไฟล์ในโพสต์หลัก

เอาท์พุท:

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

2
FGITW และใน Java ไม่น้อยกว่า คุณคุณมีคะแนนของฉัน
Patrick Roberts

2
ดี! คุณกำจัด26,609ตัวละคร
R. Kap

@ R.Kap ไปคิด! ฉันไม่ได้คิดที่จะคำนวณว่า ... ต้องมีอัลกอริทึมอย่างชาญฉลาด แต่นี่เป็นเพียงสิ่งแรกที่อยู่ในใจ ...
Socratic Phoenix

7

Python 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

Ruby, 39222 ตัวละคร

ใช้วิธีการคล้ายกับ @KarlKastor ในคำตอบ Python ของเขา แต่สตริงเริ่มต้นเป็นหนึ่งในคำที่เล็กที่สุดแทนที่จะเป็นคำที่ใหญ่ที่สุด การเพิ่มประสิทธิภาพอื่น (ฉันไม่รู้ว่ามันช่วยได้มากน้อยเพียงใด) คือในระหว่างการเติมแต่ละครั้งจะตัดคำที่อาจรวมอยู่ในสตริงเนื่องจากคำที่ทับซ้อนกัน

ทำงานในเครื่องของฉันในเวลาไม่เกิน 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) เราจะลบคำนั้นออกจากรายการคำที่เหลือ $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.