通过替换给定字符串中的"?",得到周期为 K 的字典序最小字符串

data structurec++programming

一个字符串是周期为 K 的字符串,当且仅当它每 K 个字符重复一次。例如,字符串"abcabcabc"的周期为 3,因为它每 3 个字符重复一次。字符串"abcabc?abc"不是周期为 3 的字符串,因为字符"?"并非每 3 个字符重复一次。

问题描述

给定一个包含 N 个小写字符和一个正整数 K 的字符串"str",目标是替换所有出现的字符"?"。在字符串"str"中,用小写字母填充,使得生成的字符串形成一个长度为 K 的周期。如果找不到满足给定条件的字符串,程序应输出"-1"。

字符串 str 可以包含任意数量的小写字符,包括"a"、"b"、"c"……"z"。

字符串 str 还可以包含字符"?"。

正整数 K 必须大于或等于 1。

如果找不到满足给定条件的字符串,程序应输出"-1"。

示例

输入

"aabb????", K = 4

输出

aabbaabb

输入

"xxyy????", K = 2

输出

-1

解决方法

一种直观的查找修改后字符串的方法可以通过以下步骤实现。

生成给定字符串中"?"字符的所有可能替换组合。

  • 对于每个组合,相应地替换"?"字符。

  • 检查结果字符串的周期是否为 K。如果是,则将其存储为候选解。

  • 检查所有组合后,比较候选解并选择字典顺序最小的解。

  • 返回字典顺序最小的周期为 K 的字符串,如果未找到有效解,则返回"-1"。

简单方法涉及生成所有可能的组合,这可能计算量很大。由于字符串的长度和"?"字符的数量随着字符的增加,组合的数量呈指数增长。因此,这种方法对于较大的输入字符串效率不高。

一种更优化的方法是使用一种高效的算法,避免穷举生成组合,直接根据某些条件修改字符串,以获得字典序最小的、周期为 K 的字符串。

算法

  • 接受一个字符串 str 和一个整数 K 作为参数。

  • 验证字符串"str"的长度是否是 K 的倍数。如果不是,则返回"-1",因为不可能形成长度为 K 的周期。

  • 使用变量"i"在 0 到 K 的范围内进行迭代。

  • 创建一个名为"char_freq"的空映射来存储字符的频率计数。

  • 从索引"i"开始遍历字符串"str",增量为每次迭代中返回 K 个字符。

  • 更新"char_freq"映射中遇到的每个字符的频率计数。

  • 如果 char_freq 的大小大于 2,则返回 -1。

  • 如果 char_freq 的大小为 1,则检查"?"的频率是否不为零。如果是,则将 str 中位置 i、i+K、i+2K 等处的所有"?"字符替换为"a"。

  • 否则,如果 char_freq 的大小为 2,则查找除"?"之外的字符并将其存储在 ch 中。

  • 将所有"?"替换为用 ch 修饰 str 中位于 i、i+K、i+2K 等位置的字符。

  • 返回修改后的字符串 str。

示例:C++ 程序

代码片段"stringNew()"旨在接受字符串"str"和整数"K"作为输入。首先,该函数验证"str"的长度是否能被"K"整除。如果不能,则返回 -1。但是,如果满足长度条件,则函数继续在 [0, K] 范围内迭代。对于此范围内的每个索引"i",该函数都会创建一个名为"char_freq"的映射,以跟踪子字符串"str[i: i + K]"中每个字符的频率。然后,该函数检查"char_freq"的大小是否超过 2。如果条件成立,则返回 -1。另一方面,如果条件不满足,函数将使用存储在"char_freq"中的最常见字符替换子字符串中所有出现的"?"。最后,函数返回修改后的"str"。

示例

#include <iostream>
#include <string>
#include <map>
using namespace std;

string stringNew(string &str, int K){
    // 验证字符串 "str" 的长度是否可以被 K 整除。
    if (str.length() % K != 0){
    return "-1";
    }
    // 在区间 [0, K] 内执行迭代。
    for (int i = 0; i < K; i++){
        // 创建一个 map 来保存子字符串 str[i: i + K] 中字符的频率。
        map char_freq;
        // 遍历字符串,每次迭代以 K 为增量。
        for (int j = i; j < str.length(); j += K){
            char_freq[str[j]]++;
        }
        // 如果子字符串中有两个以上的不同字符。
        if (char_freq.size() > 2){
            return "-1";
        }
        // 如果子字符串中只有一个字符。
        else if (char_freq.size() == 1){
         // 如果字符是"?",则将子字符串中所有出现的"?"替换为"a"。
         if (char_freq['?'] != 0){
            for (int j = i; j < str.length(); j += K){
               str[j] = 'a';
            }
         }
      }
      // 如果子字符串中有两个不同的字符。
      else{
         // 查找除'?'之外的字符。
         char ch;
         for (auto &x : char_freq){
            if (x.first != '?'){
               ch = x.first;
            }
         }
         // 将子字符串中所有出现的"?"与 ch 交换。
         for (int j = i; j < str.length(); j += K){
            str[j] = ch;
         }
      }
   }
   // 返回修改后的字符串。
   return str;
}
int main(){
   string str = "aabb????";
   int K = 4;
   cout << "original string: "<< str << " and "<< "K = 4" <<endl;
   cout <<"new string: "<< stringNew(str, K) << endl;
   return 0;
}

输出

original string: aabb???? and K = 4
new string: aabbaabb

时间和空间复杂度分析

时间复杂度:O(N)

  • 代码由嵌套循环组成。外层循环在 [0, K] 范围内迭代,内层循环以 K 为增量迭代字符串。

  • 外层循环的复杂度为 O(K),因为它迭代了 K 次。

  • 内层循环迭代字符串的一部分,具体来说是位置 i、i+K、i+2K 等处的字符,直到字符串末尾。内循环的迭代次数由字符串的长度 N 和 K 的值决定。

  • 总体而言,代码的时间复杂度为 O(K * N/K) = O(N)。

  • 代码分配了额外的空间来存储子字符串中每个位置"i"处的字符频率。此存储是通过名为"char_freq"的映射来实现的。

  • "char_freq"映射用于记录子字符串中遇到的字符的频率。由于子字符串最多可包含两个字符(包括"?"和另一个字符),因此该映射将仅存储这些不同字符的频率。

  • 映射"char_freq"所需的空间与子字符串中不同字符的数量成正比,在本例中最多为2个。

  • 因此,代码的空间复杂度可以视为O(1),因为空间使用量保持不变,并且不受输入大小的影响。

结论

本文提供了一种简单且高效的方法来解决这个问题。本文提供了解决方法、所使用的算法以及C++程序解决方案,并对其时间复杂度和空间复杂度进行了深入分析。


相关文章