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