C++ 将字符串的字符排列,使得最大数量的字符大于其相邻字符
在各种问题解决方案中,操纵字符串是至关重要的。发现给定字符串的排列,优化大于其连续对应字符的字符计数,是一个有趣的难题,需要重新排列字符串的字符,以生成尽可能多的相邻字符组合,其中左侧字符小于右侧字符。
方法
有几种方法可以解决字符串的排列问题,其中字符数超过相邻字符。
方法1 – 带有剪枝的回溯算法
方法2 – 动态规划
方法3 – Heap’s算法
方法4 – 带有剪枝的字典序排列
方法1:带有剪枝的回溯算法
- 使用回溯算法生成字符串的所有排列。
-
在每一步中,检查当前排列是否具有比迄今为止找到的最大值更多的大于其相邻字符的字符。
-
如果没有,剪枝并提前回溯以避免不必要的计算。
语法
function backtrack_permutation(string):
n = length(string)
max_count = [0]
- 存储大于相邻字符的字符的最大计数
result = [None] * n
- 存储最终排列。
function backtrack(curr_permutation, used_chars):
nonlocal max_count
if length(cu permutation) == n:
- 计算大于相邻字符的字符数量
count = 0
for i in range(1, n - 1):
if cu permutation [i - 1] < cu permutation[i] > cu permutation [i + 1]:
count += 1
if count > max count [0]:
max count [0] = count
result [:] = cu permutation
- 更新结果
return
for i in range(n):
if not used_chars[i]:
- 选择下一个字符
used_chars[i] = true
curr_permutation.append(string[i])
- 回溯到下一个位置
backtrack(curr_permutation, used_chars)
- 撤销选择
used_chars[i] = false
curr_permutation.pop()
- 启动回溯过程
used_chars = [false] * n
- 跟踪已使用的字符
curr_permutation = []
backtrack(curr_permutation, used_chars)
return result.
步骤
步骤1 - 从一个空字符串开始max_permutation。
- 应定义辅助函数backtracks(current_permutation,remaining_characters)。
步骤2 - 如果字符串remaining_characters为空 –
- 如果current_permutation的长度长于max_permutation的长度,则将max_permutation设置为current_permutation。
-
返回。
步骤3 - 逐个遍历remaining_characters中的每个字符c –
- 将c添加到current_permutation中以创建new_permutation。
-
如果new_permutation的长度大于1,并且其最后一个字符不再比其前一个字符长,则跳过此次迭代。
-
将c从remaining_characters中取出,生成new_remaining。
-
不断调用backtracks(new_permutation,new_remaining)。
步骤4 - 调用回溯函数,以输入文本作为remaining_characters,以空字符串作为current_permutation。
步骤5 - 提供输出max_permutation。
示例1
此程序通过最初将输入字符串”abcd”按升序排列来操作。随后,使用回溯函数生成每个可能的排列,该函数仅考虑大于前一个字符的字符,从而避免了不符合标准的潜在重复排列。此外,isValidPermutation函数根据前一个字符评估每个字符,对于与前一个字符相等或小于其的任何情况,返回false。
因此,这个有目的的过程创建了所有有效的排列,其中每个字符的数量均超过其相邻字符。我们可以自由地进一步自定义给定的输入字符串、代码和逻辑,以满足个人需求。
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int maxCount = 0;
bool isValidPermutation(const string& str) {
for (int i = 1; i < str.length(); i++) {
if (str[i] <= str[i - 1]) {
return false;
}
}
return true;
}
void backtrack(string& str, int pos) {
if (pos == str.length()) {
if (isValidPermutation(str)) {
cout << str << endl;
}
return;
}
for (int i = pos; i < str.length(); i++) {
swap(str[pos], str[i]);
if (str[pos] > str[pos - 1]) {
backtrack(str, pos + 1);
}
swap(str[pos], str[i]);
}
}
int main() {
string input = "abcd";
sort(input.begin(), input.end()); // Sort the input string initially
backtrack(input, 1);
return 0;
}
输出
abcd
方法2:动态规划
- 使用动态规划逐步生成字符串的排列。
-
从一个空的前缀开始,迭代地向其中添加字符,考虑所有可能的位置。
-
维护当前前缀中大于其相邻字符的字符数目。
-
剪枝,其中计数已经低于迄今为止找到的最大值。
语法
def find_max_permutation(string):
n = len(string)
dp = [0] * n
dp[0] = 1
- 动态规划循环
for i in range (1, n):
- 检查当前字符是否大于其相邻的字符
if string[i] > string[i-1]:
- 如果是,则增加计数器1个
dp[i] = dp[i-1] + 1
else:
- 如果不是,则数量相同
dp[i] = dp[i-1]
- 在dp数组中找到最大计数
max_count = max(dp)
return max_count
步骤
步骤 1 - 创建一个名为maxPerm(str)的函数,接受字符串作为输入,并返回满足指定条件的最长排列字符串。
步骤 2 - 首先初始化一个长度为n的数组dp,其中n等于输入字符串str的长度。在每个元素dp[i]中存储以位置i结尾的最大排列字符串。
步骤 3 - 将dp[0]初始化为字符串str的第一个字符。
步骤 4 - 遍历字符串str的字符,从索引1到n-1。
- 初始化一个空字符串curr来存储当前最大排列字符串。
-
对于每个索引i处的字符,与前一个索引i-1处的字符进行比较。
-
如果str[i]大于str[i-1],则将str[i]添加到curr。
-
否则,将str[i-1]添加到curr。
-
更新dp[i]为dp[i-1]和curr中的最大值。
步骤 5 - 循环结束后,最大排列字符串将存储在dp[n-1]中。
步骤 6 - 将dp[n-1]作为结果返回。
示例2
在这个示例中,输入字符串被硬编码为”abcbdb”。findMaxPermutation函数使用动态规划来计算每个索引处大于其相邻字符的字符的最大数量。然后通过回溯表格重构具有最大计数的字符串。最终的最大排列字符串在main函数中被打印出来。
#include <iostream>
#include <string>
#include <vector>
std::string findMaxPermutation(const std::string& str) {
int n = str.length();
// make a table to store the maximum count of characters
// larger than their adjacent characters
std::vector<std::vector<int>> dp(n, std::vector<int>(2, 0));
// Initialize the table for the base case
dp[0][0] = 0; // Count when str[0] is not included
dp[0][1] = 1; // Count when str[0] is included
// Calculate the maximum count for each index
for (int i = 1; i < n; i++) {
// When str[i] is not involved, the count is the maximum
// when str[i-1] is included or not
dp[i][0] = std::max(dp[i-1][0], dp[i-1][1]);
// When str[i] is involved, the count is the count when
// str[i-1] is not included plus 1
dp[i][1] = dp[i-1][0] + 1;
}
// The more count will be the largest of the last two values
int maxCount = std::max(dp[n-1][0], dp[n-1][1]);
// Reconstruct the string with the maximum count
std::string maxPerm;
int i = n - 1;
int count = maxCount;
// Start from the end and check which character to include
while (i >= 0) {
if ((dp[i][0] == count - 1 && dp[i][1] == count) || (dp[i][0] == count && dp[i][1] == count)) {
maxPerm = str[i] + maxPerm;
count--;
}
i--;
}
return maxPerm;
}
int main() {
std::string str = "abcbdb";
std::string maxPerm = findMaxPermutation(str);
std::cout << "String: " << str << std::endl;
std::cout << "Max Permutation: " << maxPerm << std::endl;
return 0;
}
输出
String: abcbdb
Max Permutation: bbb
方法3:Heap算法
- 实现Heap算法,高效生成字符串的所有排列。
-
在生成每个排列后,计算大于相邻字符的字符数量。
-
跟踪迄今找到的最大数量,并根据需要进行更新。
语法
function generatePermutations(string):
n = length(string)
characters = array of n elements initialized with string's characters
generatePermutationsHelper(n, characters)
function generatePermutationsHelper(n, characters):
if n = 1:
checkAndPrintPermutation(characters)
else:
for i = 0 to n-1:
generatePermutationsHelper(n-1, characters)
if n is even:
swap characters[i] and characters[n-1]
else:
swap characters [0] and characters[n-1]
步骤
步骤1 - 初始化一个数组来保存输入字符串的字符。
步骤2 - 创建一个名为“generatePermutations”的函数,带有两个参数:一个名为’size’的最终变量,确定数组大小,一个名为’arr’的数组,包含字符串字符。
步骤3 - 如果大小为1,则通过将数组中的字符组合在一起打印当前的排列,直到最大字符数超过连续字符的数目。
步骤4 - 如果不是,则函数返回。我们使用一个名为’i’的变量来迭代从索引0到’size – 1’的数组。
步骤5 - 在这个迭代中,我们进一步通过generatePermutations函数进行迭代,参数为size – 1和error。
步骤6 -如果大小为奇数,则将数组中索引0的元素替换为索引“size – 1”的元素。
步骤7 - 同样地,如果大小为偶数,则将数组中索引’i’的元素替换为索引’size – 1’的元素。
步骤8 - 最后,我们使用初始的数组大小和数组本身作为参数来调用“generatePermutations”函数。
示例1
以下C++示例使用Heap的算法创建字符串的排列,并确定相邻字符中字符数最多的排列 –
为了说明,本示例中使用“abcd”作为输入字符串。可以修改变量以使用不同的输入字符串。如果排列满足具有大于其邻居的最大字符数的要求,则会找到isValidPermutation函数是否有效。generatePermutations函数使用堆栈方法来跟踪具有最多字符的排列,以便它可以生成输入字符串的每个可能排列。主要函数打印最大数和排列本身作为输出。
#include <iostream>
#include <algorithm>
using namespace std;
// Function to check if the permutation satisfies the condition
bool isValidPermutation(const string& perm) {
int n = perm.length();
for (int i = 0; i < n - 1; i++) {
if (abs(perm[i] - perm[i + 1]) <= 1)
return false;
}
return true;
}
// Function to swap two characters in a string
void swapChar(char& a, char& b) {
char temp = a;
a = b;
b = temp;
}
// Heap's Algorithm for generating permutations
void generatePermutations(string& str, int n, int& maxCount, string& maxPerm, int idx = 0) {
if (idx == n - 1) {
if (isValidPermutation(str)) {
int count = count_if(str.begin(), str.end(), [](char c) {
return isalpha(c) && c >= 'A' && c <= 'Z';
});
if (count > maxCount) {
maxCount = count;
maxPerm = str;
}
}
return;
}
for (int i = idx; i < n; i++) {
swapChar(str[idx], str[i]);
generatePermutations(str, n, maxCount, maxPerm, idx + 1);
swapChar(str[idx], str[i]);
}
}
int main() {
string str = "abcd";
int n = str.length();
int maxCount = 0;
string maxPerm;
generatePermutations(str, n, maxCount, maxPerm);
if (maxCount == 0) {
cout << "No valid permutation found." << endl;
} else {
cout << "Maximum number of characters greater than adjacent characters: " << maxCount << endl;
cout << "Permutation with the maximum count: " << maxPerm << endl;
}
return 0;
}
输出
No valid permutation found.
方法4:带有剪枝的字典排序
- 按照字典顺序对字符串的字符进行排序。
-
生成排序后字符串的全排列。
-
在每一步中,检查当前排列是否满足最多字符大于其相邻字符的条件。
-
如果不满足条件,则跳过具有相似前缀的其余排列,以避免不必要的计算。
语法
- 生成字符串的所有排列的函数
function generatePermutations(string):
-
任务:实现排列组合生成函数
-
检查字符是否大于其相邻字符的函数
function isGreaterAdjacent(char1, char2):
-
TODO: 比较逻辑的实现
-
查找包含相邻字符更大数量的排列的函数
function findMaxAdjacentPermutation(string):
- 生成字符串的所有排列
permutations = generatePermutations(string)
- 初始化变量
max_permutation = ""
max_count = 0
- 迭代遍历每个排列
for permutation in permutations:
count = 0
- 循环遍历排列中的每个字符(不包括最后一个字符)
for i from 0 to length(permutation) - 2:
char1 = permutation[i]
char2 = permutation[i + 1]
- 检查当前字符是否大于其相邻字符
if isGreaterAdjacent(char1, char2):
count = count + 1
- 检查当前排列是否具有比先前最大值更大的计数
if count > max_count:
max_permutation = permutation
max_count = count
- 返回具有最大计数的排列
return max_permutation
步骤
步骤 1 - 以输入字符串开始。
步骤 2 - 对字符串按字典顺序排序,得到初始排列。
步骤 3 - 初始化变量 maxCount 为 0,以跟踪字符大于相邻字符的最大计数。
步骤 4 - 初始化变量 maxPermutation,用于存储具有最大计数的排列。
步骤 5 - 当存在下一个排列时 –
- 初始化变量 count 为 0,以跟踪当前字符大于相邻字符的计数。
-
对于当前排列中的每个字符 –
- 检查当前字符是否大于其前一个字符和后一个字符(如果存在)。
-
如果条件满足,则计数增加 1。
-
如果计数大于 maxCount –
- 更新 maxCount 为当前计数。
-
更新 maxPermutation 为当前排列。
步骤 6 - 将 maxPermutation 作为结果返回。
示例1
为了简单起见,我们考虑一个固定的字符串 “abcde” 来进行这个示例。
在这个示例中,函数 countAdjacentGreater 计算字符串中相邻字符大于其前字符的数量。函数 findMaxPermutation 生成输入字符串的所有排列,并检查每个排列以找到具有最大相邻字符计数的排列。
主函数初始化输入字符串 “abcde” 和用于跟踪最大计数和最大排列的变量。它调用 findMaxPermutation 函数来找到最大排列。
#include <iostream>
#include <algorithm>
#include <string>
using namespace std;
int countAdjacentGreater(const string& str) {
int count = 0;
for (int i = 0; i < str.length() - 1; i++) {
if (str[i] < str[i + 1]) {
count++;
}
}
return count;
}
void findMaxPermutation(string& str, int& maxCount, string& maxPerm) {
sort(str.begin(), str.end());
do {
int count = countAdjacentGreater(str);
if (count > maxCount) {
maxCount = count;
maxPerm = str;
}
} while (next_permutation(str.begin(), str.end()));
}
int main() {
string str = "abcde";
int maxCount = 0;
string maxPerm;
findMaxPermutation(str, maxCount, maxPerm);
cout << "String with the maximum number of characters greater than its adjacent characters: " << maxPerm << endl;
cout << "Count of adjacent characters greater in the maximum permutation: " << maxCount << endl;
return 0;
}
输出
String with the maximum number of characters greater than its adjacent characters: abcde
Count of adjacent characters greater in the maximum permutation: 4
结论
总之,找出字符串中字符最多且大于相邻字符的排列方式是字符串操作中的一个有趣挑战。通过对给定字符串进行分析并巧妙地重新排列其字符,可以实现所需的排列方式。这个问题强调了在处理字符串和排列时需要进行仔细审查和创造性思考的重要性。