HDU6586
简单贪心题目
满足要求的字典序最小的子序列
优先级1字典序 优先级2位置靠前
因为要求是子序列 很容易想到要记录前缀和后缀和等等。
h[i][j]表示位置i之后的字符(a+‘j’)的数量
pos[i]记录了字符(a+‘i’)的所有位置集合
have[i]表示当前已经有字符(‘a’+i)的数量
有了这三个 就可以开心的贪心了
贪心需要满足的两个条件有:
1.当前选择的位置i后面剩余的任意字符的数量要满足
(h[i][j]>=l[j]-have[j])
2.当前选择的位置i后面可选择的所有字符的总和大于待拼凑的长度
∑
i
=
0
25
m
a
x
(
r
[
i
]
−
h
a
v
e
[
i
]
,
0
)
>
=
k
′
\displaystyle\sum_{i=0}^{25}max(r[i]-have[i],0)>=k'
i=0∑25max(r[i]−have[i],0)>=k′
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+50;
char s[maxn],s1[maxn];
int h[maxn][26],haved[26],k;
int l[26],r[26];
vector<int>pos[26];
int main()
{
while (~scanf("%s%d",s,&k)) {
memset(haved, 0, sizeof(haved));
memset(s1,0,sizeof(s1));
int len=(int)strlen(s);
memset(h[len], 0, sizeof(h[len]));
for(int i=0;i<26;i++)
pos[i].clear();
for(int i=0;i<26;i++)
scanf("%d%d",&l[i],&r[i]);
for(int i=len-1;i>=0;i--) //记录后缀
for(int j=0;j<26;j++)
h[i][j]=h[i+1][j]+(s[i]=='a'+j);
for(int i=0;i<len;i++)
pos[s[i]-'a'].push_back(i); //存储对应字符的所在位置
int last=-1;
vector<int>::iterator iter[26];
for(int i=0;i<26;i++) iter[i]=pos[i].begin();
for(int i=0;i<k;i++){
bool flag1=0;
if(strlen(s1)==k)
break;
for(int j=0;j<26;j++)
{
if(haved[j]==r[j]) continue;
while(iter[j]!=pos[j].end()&&(*iter[j])<=last) iter[j]++;
if(iter[j]==pos[j].end()) continue;
haved[j]++;
bool flag2=0;
int p=*iter[j],sum=0;
for(int m=0;m<26;m++)
{
if(h[p+1][m]+haved[m]<l[m])
{
flag2=1;
}
}
int summ=0;
for(int m=0;m<26;m++)
{
summ+=max(0,l[m]-haved[m]);
}
if(summ>=k-i)
flag2=1;
summ=0;
for(int m=0;m<26;m++)
{
summ+=min(h[p+1][m],r[m]-haved[m]);
}
if(summ<k-i-1)
flag2=1;
if(flag2==1)
haved[j]--;
else
{
s1[i]='a'+j;
flag1=1;
last=p;
break;
}
}
if(flag1==0)
{
printf("-1\n");
goto end;
}
}
s1[k]=0;
printf("%s\n",s1);
end:;
}
return 0;
}