首页 > 其他分享 >C. Decreasing String

C. Decreasing String

时间:2023-10-13 23:11:51浏览次数:36  
标签:字符 String int Decreasing 字符串 字典 个字符 string

C. Decreasing String

Recall that string $a$ is lexicographically smaller than string $b$ if $a$ is a prefix of $b$ (and $a \ne b$), or there exists an index $i$ ($1 \le i \le \min(|a|, |b|)$) such that $a_i < b_i$, and for any index $j$ ($1 \le j < i$) $a_j = b_j$.

Consider a sequence of strings $s_1, s_2, \dots, s_n$, each consisting of lowercase Latin letters. String $s_1$ is given explicitly, and all other strings are generated according to the following rule: to obtain the string $s_i$, a character is removed from string $s_{i-1}$ in such a way that string $s_i$ is lexicographically minimal.

For example, if $s_1 = \mathrm{dacb}$, then string $s_2 = \mathrm{acb}$, string $s_3 = \mathrm{ab}$, string $s_4 = \mathrm{a}$.

After that, we obtain the string $S = s_1 + s_2 + \dots + s_n$ ($S$ is the concatenation of all strings $s_1, s_2, \dots, s_n$).

You need to output the character in position $pos$ of the string $S$ (i. e. the character $S_{pos}$).

Input

The first line contains one integer $t$ — the number of test cases ($1 \le t \le 10^4$).

Each test case consists of two lines. The first line contains the string $s_1$ ($1 \le |s_1| \le 10^6$), consisting of lowercase Latin letters. The second line contains the integer $pos$ ($1 \le pos \le \frac{|s_1| \cdot (|s_1| +1)}{2}$). You may assume that $n$ is equal to the length of the given string ($n = |s_1|$).

Additional constraint on the input: the sum of $|s_1|$ over all test cases does not exceed $10^6$.

Output

For each test case, print the answer — the character that is at position $pos$ in string $S$. Note that the answers between different test cases are not separated by spaces or line breaks.

Example

input

3
cab
6
abcd
9
x
1

output

abx

 

解题思路

  我的做法跟官方的不一样,先给出我的思路。

  对于给定 $m$,假设其表示第 $k$ 个子串 $s_k$ 中的 第 $x$ 个字符,那么问题就等价于在 $s_1$ 中删除 $k-1$ 个字符后所得到的字符串中,字典序最小的字符串的第 $x$ 个字符是什么。而题目有限制 $s_{i}$ 的获得是通过删除 $s_{i-1}$ 的一个字符所得到的所有字符串中字典序最小的那个,因此我们还需要证明通过这个限制得到的 $s_k$ 一定是从 $s_1$ 中删除 $k-1$ 个字符后得到的字典序最小的字符串,才能保证上面的问题与原问题是等价的。

  归纳法,显然 $s_2$ 是从 $s_1$ 中删除 $1$ 个字符后得到的字典序最小的字符串。假设 $s_{k-1}$ 仍然满足这个条件,由于 $s_{k-1}$ 是从 $s_1$ 中删除 $k-2$ 个字符后得到的字典序最小的字符串,而 $s_k$ 是从 $s_{k-1}$ 中删除一个字符得到的字典序最小的字符串,因此 $s_k$ 一定也是从 $s_1$ 中删除 $k-1$ 字符得到的字典序最小的字符串。

  而从 $s_1$ 中删除 $k-1$ 个字符后所得到的字典序最小的字符串,还可以等价于从 $s_1$ 中选择 $n-k+1$ 个字符所构成的字典序最小的字符串。因此要在 $s_1$ 的 $[1,n]$ 中选择 $n-k+1$ 个字符,那么对于 $s_k$ 的第 $1$ 个字符串,必然是从 $s_1$ 的 $i \in [1,k]$ 中选择最靠左且最小的字符 $s_{1,i}$(否则如果选择的 $s_{1,i}$ 有 $i > k$,那么不可能选到 $n-k+1$ 个字符,因为 $n-i+1 < n-k+1$)。同理,此时要在 $s_1$ 的 $[i+1,n]$ 中选择 $n-k$ 个字符,那么对于 $s_k$ 的第 $2$ 个字符串,必然是从 $s_1$ 的 $j \in [i+1,k+1]$ 中选择最靠左且最小的字符 $s_{1,j}$,以此类推。当选择完第 $x$ 个字符时,那么这个字符就是 $S$ 中的第 $m$ 个字符。

  可以发现上面的过程本质就是求某个区间的最小值,可以用线段树来维护。

  AC 代码如下,时间复杂度为 $O(n \log{n})$:

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

typedef long long LL;

const int N = 1e6 + 10;

char s[N];
struct Node {
    int l, r, mn;
}tr[N * 4];

void build(int u, int l, int r) {
    tr[u] = {l, r};
    if (l == r) {
        tr[u].mn = l;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        if (s[tr[u << 1].mn] <= s[tr[u << 1 | 1].mn]) tr[u].mn = tr[u << 1].mn;
        else tr[u].mn = tr[u << 1 | 1].mn;
    }
}

int query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].mn;
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    else if (l > mid) return query(u << 1 | 1, l, r);
    int x = query(u << 1, l, r), y = query(u << 1 | 1, l, r);
    if (s[x] <= s[y]) return x;
    return y;
}

void solve() {
    LL m;
    scanf("%s %lld", s + 1, &m);
    int n = strlen(s + 1), k;
    for (int i = 1; i <= n; i++) {    // 求出S的第m个字符是在哪个子串
        if (m <= n - i + 1) {
            k = i;
            break;
        }
        m -= n - i + 1;
    }
    build(1, 1, n);
    for (int i = 1, j = 0; i <= n - k + 1; i++) {
        j = query(1, j + 1, k + i - 1);    // 求出这个区间内最靠左且最小的字符
        if (i == m) {
            printf("%c", s[j]);
            return;
        }
    }
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

  下面给出官方的做法。

  考虑应该删除 $s_1$ 中的哪个字符,使得 $s_2$ 的字典序最小。可以证明应该选择最靠左第一个满足 $s_{1,i} > s_{1,i+1}$ 的字符 $s_{1,i}$,因为删除后得到的 $s_{2,i}$ 会变小(即变成了 $s_{1,i+1}$)。假设删除的是 $[i+1,n]$ 中的字符,那么得到的 $s_{2,i}' = s_{1,i} > s_{2,i} = s_{1,i+1}$,字典序不会变小。假设删除的是 $[1,i-1]$ 中的字符,由于有 $s_{1,1} \leq s_{1,2} \leq \cdots \leq s_{1,i}$,那么得到的 $s_{2}'$ 的前 $i-1$ 个字符中可能存在某个字符大于 $s_{2}$ 中与之对应的字符,而第 $i$ 个位置后的字符都相同,因此 $s_{2}'$ 的字典序不可能比 $s_{2}$ 小。得证。

  为此我们可以从左到右依次枚举 $s_1$ 中的字符并用栈来维护,找到所有最靠左第一个满足 $s_{1,i} > s_{1,i+1}$ 的字符 $s_{1,i}$。当枚举到 $s_{1,i}$,此时如果栈不为空且栈顶元素大于 $s_{1,i}$,那么就将栈顶元素弹出,表示删除了满足条件的字符,不断进行这个过程,直到栈为空或者栈顶元素不超过 $s_{1,i}$,然后再把 $s_{1,i}$ 压入栈。因此从栈底到栈顶元素满足非递降的关系。

  当我们从栈中弹出了 $k-1$ 个元素(这里的 $k$ 同上面方法的 $k$),则表示已经删除了 $k-1$ 个元素,此时只需把剩余的字符都压入栈,那么栈中第 $x$ 个元素就是答案。如果弹出的元素个数小于 $k-1$,为了得到字典序最小的字符串,应该继续从栈顶弹出直到达到 $k-1$ 个,然后栈顶元素就是答案(其实就是栈中第 $x$ 个元素)。

  AC 代码如下,时间复杂度为 $O(n)$:

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

typedef long long LL;

const int N = 1e6 + 10;

char s[N];
char stk[N];

void solve() {
    LL m;
    scanf("%s %lld", s, &m);
    int n = strlen(s), k;
    for (int i = 0; i < n; i++) {
        if (m <= n - i) {
            k = i;
            break;
        }
        m -= n - i;
    }
    int tp = 0;
    for (int i = 0; i < n; i++) {
        while (k && tp && stk[tp] > s[i]) {
            tp--;
            k--;
        }
        stk[++tp] = s[i];
    }
    printf("%c", stk[m]);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Educational Codeforces Round 156 Editorial:https://codeforces.com/blog/entry/121255

标签:字符,String,int,Decreasing,字符串,字典,个字符,string
From: https://www.cnblogs.com/onlyblues/p/17763323.html

相关文章

  • Codeforces Round 685 (Div. 2) B. Non-Substring Subsequence
    对于一个长为\(n\)的\(01\)字符串\(s\)有\(n\)个询问。第\(i\)个询问被\(l_i,r_i\)描述\(1\leql_i<r_i\leqn\)。对于每个询问,你需要确定\(s\)中是否存在一个子序列等同于子串\(s[l_i\cdotsr_i]\)。显然子序列可以和子串仅有一个字符不相同。于是\(s......
  • CF1886C Decreasing String 题解
    题面\(S_n\)由\(S_{n-1}\)去掉一个字母得到,\(S=S_1+S_2+...+S_n\)给定\(S_1\)求\(S\)的第\(N\)位solution我们先考虑怎样去字母能保持字典序最小显然,我们发现如果一个字母比前面那个字母小,那么我们就要删除前面那个字母也就是我们要删除一些字母,保持剩余的字母单调......
  • @NotBlank注解String字段会报错
    一、背景项目场景:这里说下@NotEmpty、@NotBlank、@NotNull的区别:它们所在的包:javax.validation.constraints.NotEmpty、javax.validation.constraints.NotBlank、javax.validation.constraints.NotNull1.@NotNull适用于基本数据类型(Integer,Long,Double,Date等等),当@NotNull......
  • CF938F Erasing Substrings 题解
    ErasingSubstrings一个神奇的想法是设\(f_{i,j}\)表示在位置\([1,i]\)中,我们删去了长度为\(2^k(k\inj)\)的一些串,所能得到的最小字典序。使用二分加哈希可以做到\(O(n^2\log^2n)\),无法承受。发现对于状态\(f_{i,j}\),它已经确定了\(i-j\)位的串,因为所有\(\inj\)......
  • 【OPCUA】UA_String转为QString
     字符串:UA_String typedef struct{ size_t length; UA_Byte* data;}UA_String; 生成UA_String的API有三个UA_STRING,UA_STRING_ALLOC,UA_STRING_STATICUA_STRING        - 包装现有数据(实际应用中,会有一些莫名其妙的问题)UA_STRING_ALLO......
  • QT--QString的arg方法
    在QT的QString中,arg方法类似于C中的printf中使用的格式输出符(只是有点类似)。在QT5的帮助文档中,可以看出以下几点:使用arg(str1,str2,str3)这种方法进行替换。使用arg(str1).arg(str2).arg(str3)这种方法进行替换。​使用arg(int, int, int)这种方式进行替换。解释......
  • Python word'str'(字符串前缀string prefix)的种类
    Python字符串前缀(Stringprefix) r'string'r'',用法是不会对后方字符串中的转义符进行转义,如: str=r'\n'print(str)#会直接输出\n,并不会输出换行 f'string'f'',用法是对字符进行格式化就和str.format()一样,会对{}进行格式化,如: str=f'你好,{}'......
  • 2023.10.10 js.Array和js.String
    1定义数组21.vararr=newArray{1,2,3,4...};32.vararr=[1,2,3,4];4访问5arr[索引]=值67同一数组的类型可变,长度可变。89Array中的属性和方法10arr.length//获取数组长度11forEach()遍历数组中的每个有值的元素,并调用一次传入的函数12arr......
  • Redis的Java客户端——SpringDataRedis、RedisTemplate、StringRedisTemplate
     版权声明:本文为CSDN博主「我爱布朗熊」的原创文章,遵循CC4.0BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/weixin_51351637/article/details/127502799一、初步了解SpringDataRedisSpringData是Spring中数据操作的模块,包括对各种数据库的集......
  • JSONObject.toJSONString 详细介绍
    JSONObject.toJSONString详细介绍StringjsonString=JSONObject.toJSONString(sendMap,SerializerFeature.DisableCircularReferenceDetect);JSONObject.toJSONString:这是FastJSON中的一个方法,用于将Java对象转换为JSON字符串。sendMap:这是要被转换成JSON......