String

om has a string containing only lowercase letters. He wants to choose a subsequence of the string whose length is kk 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 iith letter from a to z (indexed from 11 to 2626) must in [Li,Ri][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)S(|S|≤105)and an integer k(1≤k≤|S|)k(1≤k≤|S|). 
Then 2626 lines follow, each line two numbers Li,Ri(0≤Li≤Ri≤|S|)Li,Ri(0≤Li≤Ri≤|S|). 
It's guaranteed that SS consists of only lowercase letters, and ∑|S|≤3×105∑|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

题意:给定一个字符串S和一个k,1<=k<=|S|。再给定26个小写字母的出现次数的范围[li,ri]。要求从字符串S中选出长度为k的一个满足出现次数约束的字典序最小的子序列。

思路(有点贪心的味道):先跑出每个位置的字母出现次数的后缀和,然后从前到后依次构造字符串。如果当前字符s[i]的r[i]为0,那么显然不选进入下一个判断;如果选中这个字符之后,所有字母的最小出现次数l[i]的正值之和大于剩下的长度k,那么不选择这个字符,否则比较这个字符和前一个确定的字符,如果当前的字符字典序较小,如果后面位置的前一个字符的个数大于它的最少出现次数+1,那么说明可以在后面进行选择,所以用当前字符替换掉上一个字符。直到跑出长度为k的子序列并且所有l[i]<=0。
--------------------- 
作者:qingshanfly 
来源:优快云 
原文:https://blog.youkuaiyun.com/qq_41117236/article/details/96983533 
版权声明:本文为博主原创文章,转载请附上博文链接!

#include<bits/stdc++.h>
using namespace std;
const int MAX = 1e5 + 50; 
int L[27],R[27];
int sum[MAX][30];//sum[i][j]表示i位置后面j这个字母出现的次数 
stack<int> st;
int main(){
	string s,t="";
	int k;
	cin>>s>>k;
	for(int i=0;i<26;i++){
		scanf("%d%d",&L[i],&R[i]);
	}
	memset(sum,0,sizeof(sum));
	//求出字符串s每个位置后面(包括该位置)各个字母出现的次数,后缀和 
	int len=s.length();
	for(int i=len-1;i>=0;i--){
		for(int j=0;j<26;j++){
			if(s[i]-'a'==j) sum[i][j]=sum[i+1][j];
			else  sum[i][j]=sum[i][j];
		}
	}
	//因为是否选择s[i]已经是贪心的做法,所以贪心完之后还不满足条件,一定不存在答案 
	for(int i=0;i<len&&k;i++){
		int x=s[i]-'a';
		if(!R[x]) continue; //如果这种字母的右边界为0,说明不需要该字母或者x字母取的数量到了极限 
		int cnt=0;//cnt为满足26个限制条件最小字母数
		//如果取用该字母,那么R[x]--,那么当x字母取用数等于0时,在上面的if(!R[x])时跳出
		L[x]--; R[x]--; k--; 
		for(int j=0;j<26;j++){
			if(L[j]>0){//只有L[i]>0才计入cnt 
				cnt+=L[j];
			}
		}
		if(cnt>k){//如果取用s[i]后的cnt比k大,是不允许的 
			L[x]++; R[x]++; k++;
			continue;
		}
		int j=t.length()-1; 
		while(j>=0&&s[i]<t[j]){//该字母比已取的最后以为字母小 
			x=t[j]-'a';//此时x是之前最后一位取的字母 
			//如果取代之前的最后一位字母,判断是否后面至少还有L[x]+1位x
			//没有不符合条件,不取代该字母 
			if(L[x]+1>sum[i+1][x]){ 
				break;
			}
			L[x]++;
			R[x]++;
			k++;
			t.erase(t.length()-1);
			j--;
		}
		t.append(s,i,1);
	}
	bool flag=true;
	for(int i=0;i<26;i++){
		//如果还有哪个字母最低数量没达到,false 
		if(L[i]>0){//>0要加上,不然当L[i]为负数的时候也为false 
			flag=false;
			break;
		}
	}
	if(!flag || k){
		cout<<"-1\n";
	}
	else{
		cout<<t<<"\n";
	}
	return 0;
}                                

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值