📌  相关文章
📜  在最多K次连续交换之后,按词典顺序最小的数字

📅  最后修改于: 2021-04-23 21:41:57             🧑  作者: Mango

鉴于字符串str和一个整数的K形式的数量,任务是找到可以进行最多K个连续掉期交易后形成的最小整数。

例子:

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

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

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

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

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

高效的方法:这个想法是使用Segment树Hashing 。步骤如下:

  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 positon
    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 postion 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 rigt = segFind(
        seg, pos_start, pos_end,
        mid + 1, end, 2 * curr + 2);
  
    // Return the result
    return left + rigt;
}
  
// 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 postion 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)