C ++ 11, 38272 chữ cái, đã được chứng minh tối ưu
Thuật toán này được đảm bảo để cung cấp một giới hạn thấp hơn về giải pháp. Trong trường hợp này, nó có thể đạt được giới hạn dưới và đưa ra một giải pháp chữ cái 38272 tối ưu. (Điều này phù hợp với giải pháp được tìm thấy bởi thuật toán tham lam của Dave. Tôi đã ngạc nhiên và hơi thất vọng khi phát hiện ra rằng nó tối ưu, nhưng, chúng ta đang ở đó.)
Nó hoạt động bằng cách giải quyết vấn đề dòng chi phí tối thiểu trên mạng được xây dựng như sau.
- Đầu tiên, bất kỳ từ nào có trong các từ khác là dư thừa; loại bỏ chúng
- Với mỗi từ w , hãy vẽ hai nút w _0 và w _1, trong đó w _0 là nguồn có công suất 1 và w _1 là một bồn có công suất 1.
- Với mỗi tiền tố (nghiêm ngặt) hoặc hậu tố a của bất kỳ từ nào, hãy vẽ một nút a .
- Với mỗi hậu tố a của w , vẽ một cung từ w _0 đến a có công suất 1 và chi phí 0.
- Với mỗi tiền tố a của w , hãy vẽ một cung từ a đến w _1 với công suất 1 và chiều dài chi phí ( w ) - chiều dài ( a ).
Bất kỳ chuỗi có độ dài n chứa mỗi từ đều có thể được chuyển đổi thành luồng trên mạng này với chi phí tối đa là n . Do đó, luồng chi phí tối thiểu trên mạng này là giới hạn thấp hơn về độ dài của chuỗi ngắn nhất như vậy.
Nếu chúng ta may mắn và trong trường hợp này, chúng ta là một người may mắn thì sau khi chúng ta chuyển hướng dòng chảy vào w _1 trở lại từ w _0, chúng ta sẽ tìm thấy một luồng tối ưu chỉ có một thành phần được kết nối và đi qua nút cho trống chuỗi. Nếu vậy, nó sẽ chứa một mạch Euler bắt đầu và kết thúc ở đó. Một mạch Euler như vậy có thể được đọc lại dưới dạng một chuỗi có độ dài tối ưu.
Nếu chúng ta không may mắn, hãy thêm một số cung tròn giữa chuỗi trống và chuỗi ngắn nhất trong các thành phần được kết nối khác để đảm bảo rằng mạch Euler tồn tại. Chuỗi sẽ không còn nhất thiết phải là tối ưu trong trường hợp đó.
Tôi sử dụng thư viện LEMON cho dòng chảy chi phí tối thiểu và thuật toán mạch Euler. (Đây là lần đầu tiên tôi sử dụng thư viện này và tôi rất ấn tượng. Tôi chắc chắn sẽ sử dụng lại nó cho nhu cầu thuật toán đồ thị trong tương lai.) LEMON đi kèm với bốn thuật toán dòng chi phí tối thiểu khác nhau; bạn có thể thử chúng ở đây với --net
, --cost
, --cap
, và --cycle
(mặc định).
Chương trình chạy trong 0,5 giây , tạo ra chuỗi đầu ra này .
#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;
}