2020第十一届蓝桥杯C/C++ B组 字串排序

本文探讨了如何寻找特定交换次数的字符串,采用DP算法解决J字串排序问题,通过贪心策略和动态规划思想,确保找到最短及字典序最小的字符串。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

试题J 字串排序

题目提交点

点我进入官网提交题目

【问题描述】

小蓝最近学习了一些排序算法,其中冒泡排序让他印象深刻。
在冒泡排序中,每次只能交换相邻的两个元素。小蓝发现,如果对一个字符串中的字符排序,只允许交换相邻的两个字符,则在所有可能的排序方案中,冒泡排序的总交换次数是最少的。
例如,对于字符串 lan 排序,只需要 1 次交换。对于字符串 qiao 排序,总共需要 4 次交换。
小蓝找到了很多字符串试图排序,他恰巧碰到一个字符串,需要 V 次交换,可是他忘了把这个字符串记下来,现在找不到了。
请帮助小蓝找一个只包含小写英文字母且没有字母重复出现的字符串,对该串的字符排序,正好需要 V 次交换。如果可能找到多个,请告诉小蓝最短的那个。如果最短的仍然有多个,请告诉小蓝字典序最小的那个。请注意字符串中可以包含相同的字符。

【输入格式】

输入的第一行包含一个整数V,小蓝的幸运数字。

【输出格式】

题面要求的一行字符串。

【样例输入】

4
1

【样例输出】

bbaa
1

【样例输入】

100
1

【样例输出】

jihgfeeddccbbaa
1

【评测用例规模与约定】

对于 30% 的评测用例, 1 ≤ V ≤ 20。
对于 50% 的评测用例, 1 ≤ V ≤ 100。
对于所有评测用例, 1 ≤ V ≤ 10000。

思路

  • 首先我们要考虑:相同长度下,什么样的字符串符合短、字典序小而且交换次数最多?
    符合 s [ i − 1 ] ≥ s [ i ] s[i - 1] \geq s[i] s[i1]s[i]性质的字符串。
  • 然后贪心考虑完了, 题目中又有最小类字眼,所以考虑DP。
    DP为二维, f [ i ] [ j ] f[i][j] f[i][j]表示: 长度为i, 且以字母j - 1 + 'a'开头的字符串交换次数的集合。 求最大值。
    动态转移方程一般都是从前一个状态考虑, 那么 f [ i ] [ j ] f[i][j] f[i][j]就可以从 f [ k ] [ j − 1 ] k ∈ ( 1 , i − 1 ) f[k][j - 1] \quad k \in(1 , i - 1) f[k][j1]k(1,i1) 转化过来。
    因为 k < i k < i k<i,长度不够且要结果最大,所以假设他前面有(i - k)个最小的字符,那么,每次都要向后面移动k 次,所以还需要加上 k ∗ ( i − k ) k * (i - k) k(ik)
    数组只要开200就够了, 在不考虑26个字母的情况下, i ∗ ( i − 1 ) = v i * (i - 1) = v i(i1)=v , 但是题目中只能是小写字母,所以字符串中可能会出现相同的字符,那么就会将字符串长度拉长, 所以保险起见i 最大取值为200。
  • 最后输出DP具体方案。

代码

#include<bits/stdc++.h>
using namespace std;

#define _for(i, a, b) for (int i = (a); i < (b); ++i)
#define _rep(i, a, b) for (int i = (a); i <= (b); ++i)
#define For(i, a, b) for (int i = (a); i >= (b); --i)
#define debug(a) cout << #a << " = " << a << endl;
#define mod(x) (x) % MOD
#define ENDL "\n"
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef vector<int> vi;

const int N = 200 + 10, M = 30;
int f[N][M], n;

void get(int& a, int& b) {
    _for(i, 0, N) _rep(j, 1, 26) if (f[i][j] >= n) {
        a = i, b = j;
        return;
    }
}

int main()
{
#ifdef LOCAL
    freopen("data.in", "r", stdin);
#endif
    ios::sync_with_stdio(false);
    cout.tie(0), cin.tie(0);
    
    memset(f, -0x3f, sizeof f), f[0][0] = 0;
    cin >> n;
    _for(i, 0, N) _rep(j, 1, 26) _for(k, 0, i) 
        f[i][j] = max(f[i][j], f[k][j - 1] + k * (i - k));

    int len = 0, alp = 0, same = 0;
    get(len, alp);

    while (len) {
        if (alp == 1) cout << 'a';
        else if (f[len][alp - 1] >= n)
            alp--, cout << char(alp - 1 + 'a'), n -= len - 1, same = 1;
        else cout << char(alp - 1 + 'a'), n -= len - 1 - same, same++;
        len--;
    }

    return 0;
}
### 关于蓝桥杯竞赛中的字符串排序问题 在蓝桥杯竞赛中,字符串处理是一个常见考点,涉及排序、匹配以及动态规划等问题。以下是针对字符串排序的相关 C/C++ 实现及其解题思路。 #### 字符串排序的基本方法 对于字符串排序问题,通常可以采用标准库函数或者手动实现比较逻辑的方式完成。C++ 提供了 `std::sort` 函数,能够高效地对字符数或字符串进行排序[^1]。如果需要自定义排序规则,则可以通过传递比较器来实现特定需求。 ```cpp #include <iostream> #include <algorithm> int main() { std::string str; std::cin >> str; // 使用默认升序排序 std::sort(str.begin(), str.end()); std::cout << "Sorted string: " << str << std::endl; return 0; } ``` 上述代码展示了如何利用 `std::sort` 对输入字符串按字典顺序进行升序排列。 #### 复杂字符串操作案例分析 某些情况下,题目可能不仅限于简单排序,还涉及到模式匹配或其他复杂条件下的调整。例如,在引用[2]中提到的一道题目,其核心是对两个字符串之间的差异进行统计并修改原字符串以满足目标状态的要求。具体实现如下: ```cpp #include <stdio.h> #include <stdlib.h> // 定义辅助函数用于解决问题 int solve(char *a, char *b) { int count = 0; while (*a) { while (*a && *a == *b) { a++; b++; } if (*a) { *a = (*a == '*') ? 'o' : '*'; *(a + 1) = (*(a + 1) == '*') ? 'o' : '*'; count++; } } return count; } int main() { char a[1002]; char b[1002]; scanf("%s", a); scanf("%s", b); printf("%d\n", solve(a, b)); return 0; } ``` 此程序片段实现了基于给定规则的字符串转换功能,并返回所需的最小步数[^2]。虽然该例子主要关注的是字符串替换而非传统意义上的排序,但它体现了灵活运用基础算法解决实际问题的能力。 #### 动态规划的应用 当遇到更复杂的字符串问题时,比如寻找两字符串间的最长公共子序列(LCS),则需引入动态规划的思想[^4]。下面给出一个简单的 LCS 计算模板: ```cpp #include <iostream> #include <vector> using namespace std; int lcs(string X, string Y, int m, int n) { vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0)); for(int i=1;i<=m;i++) { for(int j=1;j<=n;j++) { if(X[i-1]==Y[j-1]) dp[i][j]=dp[i-1][j-1]+1; else dp[i][j]=max(dp[i-1][j], dp[i][j-1]); } } return dp[m][n]; } int main(){ string s1="AGGTAB"; string s2="GXTXAYB"; cout<<lcs(s1,s2,s1.size(),s2.size())<<"\n"; return 0; } ``` 这段代码通过构建二维表格存储中间结果,最终得出最大长度值作为答案。 ---
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值