假设我有一个子串集合,例如:
string a = {"cat","sensitive","ate","energy","tense"}
然后输出结果如下:
catensesensitivenergy
我该怎么做?
答案 0 :(得分:5)
这个问题被称为最短的常见超弦问题,并且它是NP难的,所以如果你需要一个精确的解决方案,你就不能做得更好,然后尝试所有的可能性并选择最好的。
一种可能的指数解决方案是生成输入字符串的所有排列,为每个排列贪婪地找到最短的公共超字符串(排列指定字符串的顺序,并且可以证明对于固定顺序,贪婪算法始终正常工作并选择最好的一个。
答案 1 :(得分:3)
使用user2040251建议:
#include <string>
#include <iostream>
#include <algorithm>
std::string merge_strings( const std::vector< std::string > & pool )
{
std::string retval;
for( auto s : pool )
if( retval.empty() )
retval.append( s );
else if( std::search( retval.begin(), retval.end(), s.begin(), s.end() ) == retval.end() )
{
size_t len = std::min( retval.size(), s.size() );
for( ; len; --len )
if( retval.substr( retval.size() - len ) == s.substr( 0, len ) )
{
retval.append( s.substr( len ) );
break;
}
if( !len )
retval.append( s );
}
return retval;
}
std::string shortest_common_supersequence( std::vector< std::string > & pool )
{
std::sort( pool.begin(), pool.end() );
std::string buffer;
std::string best_reduction = merge_strings( pool );
while( std::next_permutation( pool.begin(), pool.end() ) )
{
buffer = merge_strings( pool );
if( buffer.size() < best_reduction.size() )
best_reduction = buffer;
}
return best_reduction;
}
int main( int argc, char ** argv )
{
std::vector< std::string > a{"cat","sensitive","ate","energy","tense"};
std::vector< std::string > b{"cat","sensitive","ate","energy","tense","sit"};
std::vector< std::string > c{"personal","ate","energy","tense","gyroscope"};
std::cout << "best a --> \"" << shortest_common_supersequence( a ) << "\"\n";
std::cout << "best b --> \"" << shortest_common_supersequence( b ) << "\"\n";
std::cout << "best c --> \"" << shortest_common_supersequence( c ) << "\"\n";
return 0;
}
输出:
best a --> "catensensitivenergy"
best b --> "catensensitivenergy"
best c --> "atensenergyroscopersonal"
答案 2 :(得分:1)
打破问题,看看我们得到了什么。从只有两个字符串开始。我们必须检查一个字符串的哪个后缀是另一个字符串的最长前缀。这为我们提供了最佳连接的顺序。
现在,通过一组 n 字,我们该怎么办?我们首先构建一个包含每个单词的 trie (每个单词的一个键)。如果一个单词与另一个单词重复,我们可以在构建前缀树时轻松标记它。
我快速实施了常规的Trie。你可以找到它here。
我们有工具来构建链接不同单词的有向图,第一个的后缀是第二个的前缀。边的重量是后缀的长度。
为此,对于输入集的每个单词 w ,我们必须看到我们可以通过后缀 w 找到哪些单词:
从那里开始设置图形,我们必须找到贯穿每个顶点的最短路径(例如,单词)仅一次。如果图表已完成,则为Traveling Salesman Problem。大多数情况下,图表都不会完整。
尽管如此,它仍然是NP难题。在更多&#34;技术&#34; 术语中,当前的问题是找到the shortest hamiltonian path of a digraph。
注意:给定哈密顿路径(如果存在)及其成本C及其起始顶点(字)W,超字符串长度由下式给出:
L super = L W + C
注意:如果两个单词没有后缀将它们链接到另一个单词,则表示图形未连接且没有哈密尔顿路径。