【问题标题】:Convert a given string into a sorted string by using minimum operations使用最少操作将给定字符串转换为排序字符串
【发布时间】:2015-06-10 16:02:42
【问题描述】:

我们得到一个任意字符串。现在,我们可以对这个字符串执行一些操作。 任何字母都可以转换为任何其他字母。现在,我们可以从字符串中选择任何字母并将其转换为任何其他字母。这将被称为一个单一的操作。

我们怎样才能将一个字符串转换成一个字符串,其字母按上述最少操作数排序?

欢迎所有解决方案,包括开箱即用的解决方案!

P.S.:这是一个例子-

Given string: dcba
We can convert this string into a sorted using at least 3 operations. The generated string can be any of the following:
dddd (3 operations)
aaaa (3 operations)
cccc (3 operations)
..
etc.

P.P.S.:正如一些人所问的,我在这里提供自己的解决方案-

蛮力解决方案之一是利用递归。当我们处于字符串的某个字符索引处时,我们要么不更改它,要么将其更改为其他字符,然后递归调用索引加一的函数。如果我们改变字符,增加编号。 1 的操作,否则按原样传递。在递归的每一步,我们可以检查字符串是否排序 - 如果是,则使用当前计数更新整体最小值,如果它小于当前计数。

【问题讨论】:

  • 这不是任何传统定义的“排序”...
  • 当然。更新了标题!
  • @Mohammad 这是错误的,因为问题要求“此类移动的最小数量”。这就像说我们可以通过在我们得到它时返回它来对数组进行排序——有一种情况是它已经排序,然后它是正确的——但仅此而已。问题不是如何让它排序,问题是如何让它以最少的移动次数排序,这里重要的输出不是排序后的字符串,而是交换次数。
  • 所以达到最小值的计算复杂度是无关紧要的?
  • 这种算法的实际应用是什么?

标签: c++ string algorithm sorting


【解决方案1】:

这个问题等价于你的字符串的longest increasing subsequence:显然,保持最大字母数不变是最佳的,并且这些字母必须形成一个递增的子序列。另一个方向的工作原理类似。

虽然 LIS 可以在一般序列上以 O(n log n) 求解,但您甚至不需要它,因为您手头有一个更简单的特殊情况,字母大小为 a = 26。您可以使用动态编程来解决 O(n·a) 中的问题:

令 f(i, j) 为以字母 j 结尾的前缀 s[0..(i-1)] 的最优解。我们有复发

f(0, j) = 0  for j = 0..25
f(i + 1, j) = [j != s[i]] + MIN(k = 0..j, f(i, k))

如果 k != j 则 [k != j] 为 1,否则为 0。通过按顺序计算表的每一行(随着 j 的增加),您可以计算 O(1) 中的最小值。

最终的解是MIN(j = 0..25, f(n, j))。您可以通过递归遵循导致最佳解决方案的DP状态来构造相应的字符串:

const int a = 'z' - 'a' + 1;
vector<array<int, a>> f;
string s = "azbzc";

void reconstruct(int i, int j) {
  if (i == 0)
    return;
  int prev_j = min_element(begin(f[i-1]), begin(f[i-1]) + j + 1) - begin(f[i-1]);
  reconstruct(i - 1, prev_j);
  cout << (char)('a' + j);
}

int main() {
  f.resize(s.size() + 1);
  int n = s.size();
  for (int i = 0; i < n; ++i) {
    int sol = f[i][0];
    for (int j = 0; j < a; ++j) {
      sol = min(sol, f[i][j]);
      f[i+1][j] = (j != s[i] - 'a') + sol;
    }
  }
  int j = min_element(begin(f[n]), end(f[n])) - begin(f[n]);
  cout << "solution: " << f[n][j] << endl;
  reconstruct(n, j);
  cout << endl;
}

输出:

solution: 2
aabbc

【讨论】:

  • 恐怕您提出的解决方案不会返回正确答案。返回值将始终为0,因为min(k = 0...j, f(i-1, k) + k!=j) 将始终为0
  • @Sankalp 是的,你是对的,基本情况是假的。现在应该修复了
  • 我仍然怀疑它能否正常工作。您如何处理 LIS 不能从索引 0 而是从其他索引(例如 4)开始的情况?您确实意识到在 for 循环中维护最小值将无济于事,因为您仅从起始索引维护它..
  • @Sankalp 我的代码不会计算 LIS,它会计算您的问题的解决方案。 IE。对于输入“zzzaaaaa”,你会得到“aaaaaaaa”。开始位置没有在重复中明确编码,因为这不是必需的。另请注意我用于 DP 状态的基于 1 的索引,以防引起混淆。递归的正确性证明并不难,你可以在 i 上使用归纳法。
  • @Vishnu begin(f[n]) 是一个指向 f[n] 的第一个元素的迭代器。 min_element 还返回一个迭代器,指向 f[n] 中的最小元素。我们将两者相减得到 f[n] 中最小元素的索引。
【解决方案2】:
// One way is to find the max length of a pattern conforming and then total length - pattern length is the min count of changes.
// 
// Here is the code to find max length of entries as sorted.
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdbool.h>


struct data_entry {
    int v;
    int w;
};

typedef struct data_entry data_t;

int
find_max_weight(const data_t *l, int cnt, int v)
{
    int ret = 0;
    int min = -1;

    l += cnt-1;

    while(cnt-- >= 0) {
        if ((l->v > min) && (l->v <= v)) {
            if (ret < l->w) {
                ret = l->w;
                min = l->v;
            }
        }
        --l;
    }
    return ret;
}

int
max_pattern_len(const char *s, int len)
{
    int i, max_len = 0;
    data_t l[len];

    for(i = 0; i < len; ++i) {
        data_t *p = l + i;

        p->v = *s++;

        p->w = find_max_weight(l, i, p->v) + 1; // +1 for self

        if (max_len < p->w) {
            max_len = p->w;
        }
    }
    for(i=0; i < len; ++i) {
        printf("{%d, %d}\n", l[i].v, l[i].w);
    }

    return max_len;
}

int
main(int argc, const char **argv)
{
    char buff[100];
    int i;

    if ((argc > 1) && (argc < sizeof(buff))) {
        int max;

        for(i = 1; i < argc; ++i) {
            buff[i-1] = atoi(argv[i]);
        }
        max = max_pattern_len(buff, argc-1);
        printf("max_pattern_len=%d min_changes=%d\n", max, argc-1-max);
    }
    return 0;
}

【讨论】:

    猜你喜欢
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 2019-10-10
    • 2012-05-30
    • 2017-02-09
    • 2019-01-05
    • 2015-05-07
    • 1970-01-01
    相关资源
    最近更新 更多