【构造】构造一个字符串满足k个子序列问题总结

简介: 【构造】构造一个字符串满足k个子序列问题总结

题目 Codeforces Subsequences

题目链接 :Codeforces Subsequences

题目大意:
在这里插入图片描述

构造一个字符串,至少包含k个 codeforces子序列,并且字符串最短。

思路:构造

假设我们要找的不是 codeforces的子序列,而是 abcde。如果一个字符串中 a字母不在最前面,那么把它移到最前面其他不变的话,就多了一个 abcde序列。所以为了让序列更多,那么只需要将所有的 a都移动到最前面即可。 b跟着 a的后面, c跟着 b的后面,其他也如此。

每个字母的排序问题已经解决了,那么现在就是每个字母有多少个的问题了。答案是我们应该让每个字母的数量尽可能接近。

证明:子序列的数量就等于 SumA * SumB * SumC * SumD * SumESumA表示a的数量,如果 SumA - SumB > 1的话,那么(SumA - 1) * (SumB + 1) > SumA * SumB ,所以为了让字符串更短,所以让每个字母的数量尽可能接近。

现在我们就可以通过循环增加每个字符的数量,一旦乘积至少为k,那么就退出循环,最后打印每个字母的数量即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, inf = 0x3f3f3f3f, mod = 998244353;
int n, k;
ll a[N], c[N];
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif

    ll k;
    cin >> k;
    string s = "codeforces";
    int n = s.size();
    vector<int> a(n, 1);
    ll sum = 1;
    for (int i = 0; sum < k; i = (i + 1) % n)
    {
        sum = sum / a[i] * (a[i] + 1);
        a[i]++;
    }
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < a[i]; j++)
            cout << s[i];
    }
    return 0;
}

小红的构造题

题目链接 :小红的构造题

题目大意:

在这里插入图片描述

构造一个字符串,至少包含k个 red子序列,并且字符串长度不超过200000

思路:构造

这道题就不能用上道题的方法了,为什么?

假设k == 1e14,那么为了让字符串最短,那么每个字目的长度为1e5,那么三个字母的长度就为3e5超过范围。为什么上道题可以那样写,因为codeforces有10个字母,每个字母长度假设有100,那么最多只需要1000个字符就能解决问题。

那这道题这么解决:

我们可以先构造这样一个字符串rererererere....,在第一个re后面加x个d,那序列个数就加了x个,如果在第二个re后面加了x个d,那么就增加了3*x子序列,依次类推,在第k个re后面加x个d,那就增加k*(k+1)*x/2个子序列。

所以为了让字符串长度越小,所以需要从后往前枚举,每次填入x个字符串即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10, inf = 0x3f3f3f3f, mod = 998244353;
ll k;
ll a[N], c[N];
signed main()
{
#ifdef Xin
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif

    cin >> k;
    if (k == 0)
    {
        cout << "a";
        return 0;
    }
    int M = 80000;
    for (int i = 1; i <= M; i++)
    {
        a[i] = (ll)i * (i + 1) / 2;
    }
    int id = 0;
    for (int i = M; i >= 1; i--)
    {
        if (k >= a[i])
        {
            c[i] = k / a[i];
            k %= a[i];
        }
    }
    for (int i = 1; i <= M; i++)
    {
        cout << "re";
        while (c[i]--)
            cout << 'd';
    }

    return 0;
}
相关文章
|
7月前
|
C#
C#的小例子和字符串(一)
C#的小例子和字符串(一)
149 0
|
6月前
|
存储 对象存储 C++
使用ostringstream处理字符串的方法详解
使用ostringstream处理字符串的方法详解
|
7月前
|
JavaScript 前端开发 API
|
7月前
2020-10-10 数组和对象的区分方法
2020-10-10 数组和对象的区分方法
|
7月前
|
数据安全/隐私保护 C++
C++ 构造函数实战指南:默认构造、带参数构造、拷贝构造与移动构造
C++中的构造函数是特殊成员函数,用于对象初始化。类型包括默认构造函数(无参数)、带参数构造函数、拷贝构造函数和移动构造函数。默认构造函数设置对象默认状态,带参数构造函数允许传递初始化值。拷贝构造函数复制已有对象,移动构造函数高效转移资源。构造函数的访问权限可控制为public、private或protected。理解构造函数有助于编写健壮的C++代码。关注公众号`Let us Coding`获取更多内容。
112 0
|
7月前
自定义封装一个方法让这个方法可以判断所有的数据类型并返回
自定义封装一个方法让这个方法可以判断所有的数据类型并返回
40 0
常用的数组(字符串)方法有哪些?(二)
concat:合并数组或者字符串,concat在项目中用的还是比较多的,最经典的就是一个表格数据是有两个或者三个数组组成的时候会用到,watch监听数组和concat结合使用。下期做一个例子。
常用的数组(字符串)方法有哪些?(三)
some:判断数组中有没有符合条件的元素,一个符合的都没有返回false,有一个就是true。
|
JavaScript
常用的数组(字符串)方法有哪些?(一)
1.pop:末位删除,即删除数组的最后一项,返回值是被删除项。 2.shift:首位删除,即删除数组的第一项,返回值是被删除项。 3.splice:指定下标删除元素,返回被删除的元素。第一个参数是从下标几开始删除,第二个参数是删除几个,第三个参数是要插入的元素。splice方法是会改变原数组的。删除功能用的比较多,我个人更喜欢用filter来变相实现删除,splice是会改变原数组的,而filter不会
|
索引
字符串方法
字符串方法
105 0