📜  最多 K 个连续交换后的字典序最小数

📅  最后修改于: 2021-10-27 08:35:40             🧑  作者: Mango

给定一个字符串str形式的数字和一个整数K ,任务是找到最多执行K个连续交换后可以形成的最小整数。

例子:

朴素的方法:最简单的想法是生成给定字符串的所有可能排列,并检查哪个字典序最小的字符串是否满足最多K 次交换的条件。打印那个字符串。

时间复杂度: O(N!),其中 N 是给定字符串长度。
辅助空间: O(1)

更好的方法:更好的方法是使用贪婪方法。以下是步骤:

  1. 如果给定数字中存在前导零,则删除前导零。
  2. 从字符串str 中选取最小的元素[K较小时考虑str[k] ,否则为N ]。
  3. 将所有这些元素向右移动 1 个位置后,将最小的元素放在第 0 个位置。
  4. K 中减去上述步骤中的交换次数。
  5. 如果仍然剩下K > 0,那么我们从下一个起始位置即str[2, …N]应用相同的过程然后将其放置到第一个位置。
  6. 所以我们继续应用相同的过程,直到K 变为 0

时间复杂度: O(N 2 ),其中 N 是给定字符串长度。
辅助空间: O(1)

高效的方法:想法是使用Segment treeHashing 。以下是步骤:

  1. 如果给定数字中存在前导零,则删除前导零。
  2. 将数字的原始位置存储在 Map 中,并在每个位移小于等于K 的索引处找到最适合的数字。
  3. 使用地图查找数字的原始位置。
  4. 找到当前位置右侧并被移位的位数,为此,标记其在段树中从[0 … N-1]移位的位置。
  5. 线段树的每个节点都包含移位的位置数。现在的任务是找出在[current_index, N-1]范围内移动了多少位置因为只有这样才会影响原始位置。
  6. 要移位的新位置将是(original_position + number_of_right – element_shifted – i),即元素的原始位置被添加到刚刚移位的线段树中。

以下是上述方法的程序:

C++
// C++ program for the above approach
#include 
using namespace std;
 
// Function to mark the pos as 1 that
// are shifted in string in Segtree node
void segAdd(vector& seg, int start,
            int end, int pos, int curr)
{
 
    // Out of Range
    if (pos < start or pos > end)
        return;
 
    // Check if we reach the position
    if (start == end) {
        seg[curr] = 1;
        return;
    }
 
    // Initialize the mid
    int mid = (start + end) / 2;
 
    // Recursion call
    segAdd(seg, start, mid, pos,
           2 * curr + 1);
 
    segAdd(seg, mid + 1, end, pos,
           2 * curr + 2);
 
    // Every node contains no of
    // marked position that are
    // shifted in a range
    seg[curr] = seg[2 * curr + 1]
                + seg[2 * curr + 2];
 
    return;
}
 
// Function to find the number of
// elements which got shifted
int segFind(
    vector& seg, int pos_start,
    int pos_end, int start, int end,
    int curr)
{
    // Return 0 is the end position is
    // less than start or the start
    // position is greater than end
    if (pos_end < start
        or pos_start > end)
        return 0;
 
    if (pos_start <= start
        and pos_end >= end)
        return seg[curr];
 
    // Initialize the mid
    int mid = (start + end) / 2;
 
    // Recursion call
    int left = segFind(
        seg, pos_start, pos_end,
        start, mid, 2 * curr + 1);
 
    int right = segFind(
        seg, pos_start, pos_end,
        mid + 1, end, 2 * curr + 2);
 
    // Return the result
    return left + right;
}
 
// Function to remove leading zeros
// from the given string str
string removeLeadingZeros(string str)
{
 
    int i = 0;
 
    // To store the resultant string
    string ans = "";
 
    for (; i < str[i]; i++) {
 
        // If Initial character is 0,
        // then remove it
        if (str[i] == '0') {
            i++;
        }
 
        // Else break
        else {
            break;
        }
    }
 
    ans = str.substr(i - 1,
                     str.length());
 
    // Return the updated string
    return ans;
}
 
// Function to find the lexiographically
// smallest integer
string findMinimumInteger(
    string arr, int k)
{
 
    // To remove leading zeros
    arr = removeLeadingZeros(arr);
 
    int n = arr.size();
 
    // Segment tree vector
    vector seg(
        (2 * (int)pow(
                 2,
                 (int)(ceil(log2(n))))
         - 1),
        0);
 
    // Hash to find the original
    // position of the digit
    unordered_map > m;
 
    for (int i = 0; i < n; i++) {
        m[arr[i] - '0'].push_back(i);
    }
 
    // Resultant string variable
    string res = "";
 
    for (int i = 0; i < n; i++) {
 
        // Place a digit from
        // 0-9 which fit best
        for (int digit = 0;
             digit <= 9; digit++) {
 
            if (m[digit].size() != 0) {
                int original_pos
                    = m[digit].front();
 
                // Find the number of
                // right elements that
                // are shifted from
                // current element
                int shift
                    = segFind(
                        seg, original_pos,
                        n - 1, 0, n - 1, 0);
 
                // Mark the new position
                int new_pos = original_pos
                              + shift
                              - i;
 
                if (new_pos <= k) {
                    k -= new_pos;
 
                    // Add the original position of
                    // the element which got shifted
                    segAdd(seg, 0, n - 1,
                           original_pos, 0);
 
                    res.push_back('0' + digit);
                    m[digit].pop_front();
                    break;
                }
            }
        }
    }
 
    // Return the result
    return res;
}
 
// Driver Code
int main()
{
    // Given string
    string s = "9438957234785635408";
 
    // Given K swaps
    int k = 23;
 
    // Function Call
    cout << findMinimumInteger(s, k)
         << endl;
}


输出:
0345989723478563548

时间复杂度: O(N * log N),其中 N 是字符串的长度。
辅助空间: O(N)