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