String
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 2421 Accepted Submission(s): 644
Problem Description
Tom has a string containing only lowercase letters. He wants to choose a subsequence of the string whose length is k and lexicographical order is the smallest. It’s simple and he solved it with ease.
But Jerry, who likes to play with Tom, tells him that if he is able to find a lexicographically smallest subsequence satisfying following 26 constraints, he will not cause Tom trouble any more.
The constraints are: the number of occurrences of the ith letter from a to z (indexed from 1 to 26) must in [Li,Ri].
Tom gets dizzy, so he asks you for help.
Input
The input contains multiple test cases. Process until the end of file.
Each test case starts with a single line containing a string S(|S|≤105)and an integer k(1≤k≤|S|).
Then 26 lines follow, each line two numbers Li,Ri(0≤Li≤Ri≤|S|).
It’s guaranteed that S consists of only lowercase letters, and ∑|S|≤3×105.
Output
Output the answer string.
If it doesn’t exist, output −1.
Sample Input
aaabbb 3
0 3
2 3
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
Sample Output
abb
Source
题意
有一个全为小写字母的字符串,用其构造一个长度为 k 的子序列字符串,目标字符串的各字母的数量必须在给定的范围 [ l , r ] 内,且构造出来的字符串字典序尽可能小,没有对应字符串输出 -1。
思路
用二维数组 cnt 记录原字符串每一位的后缀的各字母的数量,用队列 g 按照顺序记录每各个字母依次出现的下标,数组 use 当前目标字符串各字母的数量。
逐位构造目标字符串,构造时,贪心(按照字典序顺序)选取满足条件的字母即可
可以选取的条件为该位置的字母后缀的各字母的数量 大于等于 最低数量减去已经构造的字母数量 即cnt[pos][z] >= l[z] - use[z] 和 每个字母当前最低数量之和要小于当前目标字符串剩余的长度 和 该位置的字母已使用的数量要 小于 该字母目标的最大数量即use[s[pos]] < r[s[pos]] 和 这个位置的字母的后缀字母的总和要大于等于当前目标字符串剩余长度。
注意一开始如果目标字符串各字母最大数量之和小于目标字符串的长度直接输出 -1。
代码
#include <bits/stdc++.h>
#define maxn 100100
#define line cout << "--------------------" << endl;
using namespace std;
char s[maxn];
int cnt[maxn][26], l[26], r[26], use[26];
char ans[maxn];
queue<int>g[26];
int main(){
int k;
while(~scanf("%s%d", &s, &k)){
for(int i = 0; i < 26; i++){
scanf("%d%d", &l[i], &r[i]);
}
memset(cnt, 0, sizeof(cnt));
memset(use, 0, sizeof(use));
int len = strlen(s);
for(int i = len - 1; i >= 0; i--){
for(int j = 0; j < 26; j++) cnt[i][j] = cnt[i + 1][j];
cnt[i][s[i] - 'a'] += 1;
}
for(int i = 0; i < 26; i++) while(!g[i].empty()) g[i].pop();
for(int i = 0; i < len; i++) g[s[i] - 'a'].push(i);
int p = 0, sumr = 0;
for(int i = 0; i < 26; i++){
sumr += r[i];
}
if(sumr < k){
cout << -1 <<endl;
continue;
}
int last = -1;
bool mark = true;
for(int i = 0; i < k; i++){
bool flag;
for(int j = 0; j < 26; j++){
flag = true;
while(!g[j].empty() && g[j].front() <= last){
g[j].pop();
}
if(g[j].empty()) continue;
int pos = g[j].front(), sum = 0, sumcnt = 0;
if(use[j] >= r[j]) continue;
for(int z = 0; z < 26; z++){
if(cnt[pos][z] < l[z] - use[z]){
flag = false;
}
sumcnt += cnt[pos][z];
if(l[z] - use[z] > 0){
sum += l[z] - use[z];
if(j == z) sum -= 1;
}
}
if(sumcnt < k - i){
flag = false;
}
if(k - i <= sum){
flag = false;
}
if(flag){
ans[i] = s[pos];
use[j]++;
g[j].pop();
last = pos;
// cout << pos << " " ;
break;
}
}
if(!flag){
mark = false;
}
}
if(!mark){
cout << -1 << endl;
}
else{
ans[k] = '\0';
cout << ans << endl;
}
}
}