【问题标题】:Finding Lexicographically smallest arrangement of some string找到一些字符串的字典最小排列
【发布时间】:2016-09-22 09:26:27
【问题描述】:

标题似乎是一个非常常见的问题,但请多多包涵。

基本上让我们在字符串的每个索引处说您知道哪些字母可能在该索引中,然后您想找到按字典顺序排列的最小排列。

例如:

Index  |  Options
-------|----------
  1    |  'b'
  2    |  'c', 'a'
  3    |  'd', 'c'
  4    |  'c', 'a'

因此,o/p 应该是badc。是的,顺便说一句,字符不能重复,所以没有贪心算法。

我认为我们可以通过创建队列或字符串的某些内容来使用某种广度优先搜索,并且每次我们发现我们无法创建另一个排列时,您将其从列表中弹出。怀疑这是最佳的,但一定是O(N) 中的东西。有什么想法吗?

不,我不认为 C 语言不好,但我更喜欢 C/C++ 中的代码 sn-ps :p

谢谢!

【问题讨论】:

  • 要么应该是“bacd”,要么我还不明白这个问题。
  • 字符串的最大长度是多少?
  • badc的输出怎么样,能解释一下吗?
  • @sudomakeinstall2,因为字符不能重复,长度必须是
  • @MuhammadAhmad 如果索引 3 选择“c”,则索引 4 不能选择“c”,也不能选择“a”,这让你别无选择,只能选择'd' 表示索引 3。

标签: c++ algorithm permutation breadth-first-search


【解决方案1】:

这可以通过匹配算法来解决。您可以使用网络流解决方案来解决此问题。这可以分解为一个二分图问题。

准确地说是最大权重分配问题或最大成本最大匹配问题。

下面是顶点的二分集 -

LEVEL          Alphabets
 1                 a  
 2                 b
 3                 c
 4                 d
                   e
                   .
                   .
                   .
                   z 

现在,仅当且仅当这些是该级别的选项时,才将集合级别中的边分配给集合 Alphabet。所以这些将是这里的边缘 - {1,b} , {2,a}, {2,c} , {3,c} , {3,d} ,{4,a} ,{4,c} 现在,要获得字典顺序最少的结果,您需要以这种方式为边缘分配权重 -

Edge Wt. = 26^(N-Level) * ('z' - Alphabet)

例如,边 {2,c} 的边权重为

26^(4-2) * (26-3) =  26^2*23

现在您可以使用标准的最大成本最大匹配解决方案。这是一个多项式解决方案。就我现在所能想到的而言,这将是最好的方法。天真的解决方案是 26^N 的指数解决方案,所以我认为您会对多项式解决方案感到满意。

【讨论】:

  • 字符串可以长达26个字符,这使得边缘权重非常大。小心点。
  • 我想我会用这个,只是要先学习最大成本最大匹配。目前有点菜鸟:)
  • @adisticated:你是如何得到这个公式来分配边缘权重的?
【解决方案2】:

天真的方法是使用回溯并尝试所有可能的解决方案,但它的效率不够高(26!)。然后,您可以借助位掩码使用动态编程来改进此回溯解决方案。位掩码可以帮助您存储迄今为止使用过的字符。

编写一个递归函数,它接受两个输入,一个应该分配一个字符的索引,以及一个位掩码,它指示我们到目前为止使用了哪些字符。最初,位掩码包含 26 个零,这意味着我们没有使用任何字符。将字符分配给某个索引后,我们相应地更改位掩码。例如,如果我们使用字符 a,我们将位掩码的第一位设置为 1。这样您就不会解决很多重叠的子问题。

#include <iostream>
#include <queue>
#include <vector>
#include <map>
using namespace std;

vector<vector<char> > data;
map< pair<int,int>, string > dp;

string func( int index, int bitmask ){
    pair<int,int> p = make_pair(index,bitmask);
    if ( dp.count( p ) )
        return dp[p];
    string min_str = "";
    for ( int i=0; i<data[index].size(); ++i ){
        if ( (bitmask&(1<<(data[index][i]-'a'))) == 0 ){
            string cur_str = "";
            cur_str += data[index][i];
            if ( index+1 != data.size() ){
                int mask = bitmask;
                mask |= 1<<(data[index][i]-'a');
                string sub = func(index+1, mask);
                if (sub == "")
                    continue;    
                cur_str += sub;
            }
            if ( min_str == "" || cur_str < min_str ){
                min_str = cur_str;
            }
        }
    }
    dp[p] = min_str;
    return min_str;
}


int main()
{
    data.resize(4);
    data[0].push_back('b');
    data[1].push_back('c');
    data[1].push_back('a');
    data[2].push_back('d');
    data[2].push_back('c');
    data[3].push_back('c');
    data[3].push_back('a');
    cout << func(0,0) << endl;
}

【讨论】:

  • “但是效率不够”:你怎么知道?
  • 如果问题是 NP 难的,任何解决方案都可以是有效的。
  • 背包是NP。您尝试通过蛮力解决它还是使用 dp?
  • 启发式并不能改善问题的最坏情况。
  • 问题的规模有限,具有最优子结构和重叠子问题。这种方法最坏的情况是 (26)*(2^26)。当然匹配方法要好得多。
猜你喜欢
  • 1970-01-01
  • 2017-03-31
  • 2023-03-28
  • 1970-01-01
  • 1970-01-01
  • 2016-12-15
  • 1970-01-01
  • 2021-09-26
相关资源
最近更新 更多