从子字符串中制作优化字符串的算法

时间:2014-12-15 13:18:33

标签: c++ c string algorithm

假设我有一个子串集合,例如:

string a = {"cat","sensitive","ate","energy","tense"}

然后输出结果如下:

catensesensitivenergy

我该怎么做?

3 个答案:

答案 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 找到哪些单词:

  • 我们使用后缀走下trie。我们将最终进入一个节点(或不是)。
  • 从该节点开始,如果存在,我们扫描剩余的子树以查看哪些字 可用。
    • 如果给定的长度 l 的后缀与a匹配 单词 w&#39; 的前缀,然后我们添加一个边缘 w→w&#39; ,权重长度(w&#39;) - l
    • 如果此类边缘已存在,我们只需更新权重以保持最低

从那里开始设置图形,我们必须找到贯穿每个顶点的最短路径(例如,单词)仅一次。如果图表已完成,则为Traveling Salesman Problem。大多数情况下,图表都不会完整。

尽管如此,它仍然是NP难题。在更多&#34;技术&#34; 术语中,当前的问题是找到the shortest hamiltonian path of a digraph

注意:给定哈密顿路径(如果存在)及其成本C及其起始顶点(字)W,超字符串长度由下式给出:

  

L super = L W + C

注意:如果两个单词没有后缀将它们链接到另一个单词,则表示图形未连接且没有哈密尔顿路径。