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
题意:给你一个字符串和k,接下来26行给出每个字母的个数限制(l[i],r[i])。让你构造一个字典序最小的长度为k的满足限制条件的一个子序列串(可以不连续,但要符合顺序),没有则输出-1。
题解:将26个字母各自放入队列中,并记下每个位置各字母的后缀和。往k个位置贪心的放字母,先从a开始尝试。首先字母的数量不能大于最大限制,然后位置要大于上一个放的字母。要放这个字母还要满足两个条件。
1.对于26个字母,其后的个数加上当前选的个数要小于l[i]。
2.对于26个字母,把其后的个数取最小之后的和不能大于k,把其后的个数取最大之后的和不能小于k。
就这样贪心的放,再检查是否符合条件。
注意:写的时候要特别注意下标
上代码:
#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int MAX = 30;
int l[MAX],r[MAX],num[MAX];
int sum[100010][MAX];
char s[100010],ans[100010];
int len,k;
void init(){
for (int i = 0; i < 26;i++){
sum[len+1][i]=0;
num[i]=0;
}
}
int main(){
while(~scanf("%s%d",s+1,&k)){
queue<int> q[30];
len=strlen(s+1);
init();
for (int i = 0; i < 26;i++) cin >> l[i] >> r[i];
for (int i = 1; i <= len;i++) q[s[i]-'a'].push(i);//注意下标
for (int i = len; i >= 1;i--){
for (int j = 0; j < 26;j++){
sum[i][j]=sum[i+1][j]+(s[i]==('a'+j));
}
}
int now=0;
bool flag=false;
int cntt=0;
for (int i = 1; i <= k;i++){
for (int j = 0; j < 26;j++){
if(num[j]==r[j]) continue;//不能大于最大限制
while(!q[j].empty()&&now>=q[j].front()) q[j].pop();
if(!q[j].empty()){
bool f=false;
int cnt=q[j].front();
num[j]++;//先放上这个字母,再判断行不行
for (int ii = 0; ii < 26;ii++){//判断满足条件一
if(num[ii]+sum[cnt+1][ii]<l[ii]){
num[j]--;
f=true;
break;
}
}
if(f) continue;
int minn,maxx;
minn=maxx=0;
for (int ii = 0; ii < 26;ii++){//判断满足条件二
minn+=max(l[ii]-num[ii],0);
maxx+=min(r[ii]-num[ii],sum[cnt+1][ii]);
}
if(minn+i<=k&&maxx+i>=k){//判断满足条件二
ans[cntt++]=('a'+j);
now=q[j].front();
break;
}
else num[j]--;
}
}
if(cntt!=i) break;
}
if(cntt==k){
ans[k]='\0';//注意这里的'\0'!!!
printf("%s\n",ans);
}
else cout << -1 << endl;
}
return 0;
}