String(有限制的序列自动机?)

11 篇文章 0 订阅
6 篇文章 0 订阅

http://icpc.upc.edu.cn/problem.php?cid=1822&pid=8

题目描述

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.

 

 

输入

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 the answer string.
If it doesn't exist, output −1.

 

 

样例输入

复制样例数据

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

样例输出

abb

题意:求该串的一个长度为k的子序列,要求该子序列中任意字母的数目满足条件限制且字典序最小

分析:如果只是求字典序最小的长度为k的子序列可以用序列自动机(就是用队列保存字母的下标,然后贪心暴力,时间复杂度O(26*N)),但是这道题目对每一种字母的出现次数做了限制,那在贪心的时候把限制加上就好了

求长度为k的字典序子序列题目https://nanti.jisuanke.com/t/39614(好像也可以用单调栈)

//#include<bits/stdc++.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1e5+5;
char s[N];
int num[30];//已经用了多少个某字母
int last[N][26];//i位置后(包括i),还有多少个j字母
char ans[N];//存结果
int l[30],r[30];//最小数目限制,最大数目限制
int main(){
    int k;
    while(~scanf("%s%d",s+1,&k)){
        queue<int> q[30];
        int len=strlen(s+1);
        memset(num,0,sizeof(num));
        for(int i=1;i<=len;i++){
            q[s[i]-'a'].push(i);
        }
        for(int i=0;i<26;i++){
        	last[len+1][i]=0;
		}
        for(int i=len;i>=1;i--){
        	for(int j=0;j<26;j++){
        		if(s[i]=='a'+j) last[i][j]=last[i+1][j]+1;
        		else last[i][j]=last[i+1][j];
			}
		}
        for(int i=0;i<26;i++){
            scanf("%d%d",&l[i],&r[i]);
        }
        int  lp=0;//上一个确定好的字母在原串中的下标
        bool flag;
        for(int pos=1;pos<=k;pos++){//枚举每一位
        	flag=false;
        	for(int i=0;i<26;i++){
        		if(num[i]==r[i]) continue;//已经达到最大限制,该字母不能用了
        		while(!q[i].empty()&&q[i].front()<=lp) q[i].pop();//下标小于等于lp的肯定不能用了,出队
				if(!q[i].empty()){
					bool ok=true;//假设这个字母可以放在这个位置
					num[i]++;
					for(int j=0;j<26;j++){
						if(last[q[i].front()+1][j]+num[j]<l[j]) {//剩下的该字母+用了的j字母已经不能达到最小限制
							ok=false;
							break;
						} 
					}
					if(ok){
                        int lim1=0;//后面字母假设全拿最小限制,还能拿多少字母
					    int lim2=0;//后面字母假设全拿最大限制 ,还能拿多少字母
						for(int j=0;j<26;j++) lim1+=max(l[j]-num[j],0);
						for(int j=0;j<26;j++) lim2+=min(r[j]-num[j],last[q[i].front()+1][j]);
						if((lim1>k-pos)||(lim2<k-pos)) ok=false;
					}
					if(!ok) {
						num[i]--;
					}
					else{
						ans[pos]='a'+i;
						flag=true;
						lp=q[i].front();
						break;
					}
				} 
			}
			if(!flag) break;
		}
		if(!flag) printf("-1\n");
		else{
			for(int i=1;i<=k;i++){
				printf("%c",ans[i]);
			}
			printf("\n");
		}	
    }
    
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值