String【贪心】

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_43298454/article/details/98334265

题目链接

题意:
给你一个字符串S,选取长度为k的子序列(不要求连续),子序列需满足输入的26个条件,第i+1行表示字母表中第i个字母最少需要的个数以及最多需要的个数。

题解:
逐位考虑,每位取的时候需按照字典序顺序,而且必须以下条件:
1.取该字符cnt的时候必须确保该字符后面的该字符个数加上已经取该字符cnt的个数总和大于等于所需该字符的最少个数,即:

a[cnt][j] + used[cnt] >= t[cnt].l;

2.每个字母当前最低数量之和要小于当前目标字符串剩余的长度,即:

sum4 < k - i;

3.该位置的字母已使用的数量要 小于 该字母目标的最大数量,即:

used[j] < t[j].r;

4.这个位置的字母的后缀字母的总和要大于等于当前目标字符串剩余长度,即:

k - i <= sum3;

AC_Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 3e5+5;
queue<int>q[30];
//q[i]记录26个字母中第i+1个字母在母字符串中的位置 ,比如字符串“babca”,则将1,4推进q[0] 
int a[maxn][30];
//a[i][j]记录母字符串第i个字符后面(包括自己)的j字符的个数,j字符表示 26个字母中第j+1个字母
int used[30];
char s[maxn],ans[maxn];
int k,len,sum1,sum2;

struct node{
	int l;
	int r;
}t[30];

void solve(){
	len = strlen(s);
	for(int i=0; i<len; ++i){
		q[s[i]-'a'].push(i);
	}
	for(int i=len-1; i>=0; --i){
		for(int j=0; j<26; ++j){
			a[i][j] = a[i+1][j];
		}
		a[i][s[i]-'a'] += 1;
	}
	int last = -1;
	bool mark = true;
	for(int i=0; i<k; ++i){//按位存入新字符串ans 
		bool flag;
		for(int j=0; j<26; ++j){
			flag = true;
			while(!q[j].empty() && q[j].front() <= last){
				q[j].pop();
			}
			//将第last位前面的j字符去除 
			if(q[j].empty() || used[j] >= t[j].r) continue;
			int pos = q[j].front(), sum3 = 0, sum4 = 0;
			//pos是在保证字典序最小的情况下选取的字符在母字符串中的下标 
			for(int z=0; z<26; ++z){
				if(a[pos][z] < t[z].l - used[z]) flag = false;
				sum4 += a[pos][z];
				//sum4记录在pos后所有字符的总个数 
				if(t[z].l - used[z] > 0){
					sum3 += t[z].l - used[z];
					//sum3记录满足26个最少需要的总个数 
					if(j == z) sum3 -= 1;
				}
			}
			if(sum4 < k - i || k - i <= sum3){
				flag = false;
			}
			//还有k-i个字符需要存入新字符串ans 
			if(flag){
				ans[i] = s[pos];
				used[j] += 1;
				q[j].pop();
				last = pos;
				break;
			}
		}
		if(!flag) mark = false;//第i位没有满足的字符 
	}
	ans[k] = '\0';
	if(mark) cout << ans << endl;
	else puts("-1");
	
}

int main(){
	while(~scanf("%s%d",s,&k)){
		sum1 = 0; sum2 = 0;
		memset(a,0,sizeof(a));
		memset(t,0,sizeof(t));
		memset(used,0,sizeof(used));
		for(int i=0; i<26; ++i){
			scanf("%d%d",&t[i].l,&t[i].r);
			sum1 += t[i].l;//最终构建成的字符串的最小长度 
			sum2 += t[i].r;//最终构建成的字符串的最大长度 
			while(!q[i].empty()) q[i].pop();
		}
		if(sum1 > k || sum2 < k){
			puts("-1");
			continue;
		}
		solve();
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_43298454/article/details/98334265
今日推荐