HDU-6586 String 2019杭電多校第一場
我的博客:https://acmerszq.cn
原題鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6586
題意
多組輸入,每組第一行輸入一個字符串s和一個數字k,接下來每行輸入26行,每行兩個數字,,代表在長度爲k的子序列中,在這個範圍中出現。
思路
貪心,記錄每個字母對應的所有位置,每個字母已經放入的數量,每個位置後剩餘的每種字母的數目。當每個字母的使用數量與最大數量相同時跳過,字母的位置沒有到達last位置時,將位置下標一直往後移動。
還需要一個數組用於記錄字母已經用到第幾個。
貪心條件:
1:當前位置的字母已經被選中的加上剩餘的和大於等於最小出現次數;
2:求已出現次數與最少出現次數的差(小於0時記爲0),並計算出它們的和,和必須小於等於剩下的子序列未放置數量;
3:求,和必須大於等於可放置數目。
AC代碼
#include <bits/stdc++.h>
using namespace std;
const int SIZE = 1e5+5;
char str[SIZE];
int used[30];
string ansStr;
int hz[SIZE][30];
vector<int> xb[30];
vector<int>::iterator it[30];
struct node {
int l, r;
} ch[SIZE];
void init() {
memset(used, 0, sizeof(used));
memset(hz, 0, sizeof(hz));
memset(ch, 0, sizeof(ch));
for(int i = 0; i < 30; i++) {
xb[i].clear();
}
ansStr.clear();
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
while(~scanf("%s", str)) {
init();
int k = 0;
scanf("%d", &k);
int len = strlen(str);
for(int i = 0; i < 26; i++) {
scanf("%d%d", &ch[i].l, &ch[i].r);
}
for(int i = len-1; i >= 0; i--) {
for(int j = 0; j < 26; j++) {
hz[i][j] = hz[i+1][j]+(str[i]=='a'+j);
}
}
for(int i = 0; i < len; i++) {
xb[str[i]-'a'].push_back(i);
}
for(int i = 0; i < 26; i++) {
it[i] = xb[i].begin();
}
bool flag = false;
int ans = 0;
int last = -1;
for(int i = 0; i < k; i++) {
flag = false;
for(int j = 0; j < 26; j++) {
bool ok = true;
if(used[j] == ch[j].r) continue;
while((*it[j]) <= last && it[j] != xb[j].end()) it[j]++;
if(it[j] == xb[j].end()) continue;
int pos = *it[j];
int sum = 0;
used[j]++;
for(int x = 0; x < 26; x++) {
if(used[x]+hz[pos+1][x] < ch[x].l) ok = false;
sum += max(0, ch[x].l - used[x]);
}
if(sum > k - i - 1) ok = false;
sum = 0;
for(int x = 0; x < 26; x++) {
sum += min(hz[pos+1][x], ch[x].r - used[x]);
}
if(sum < k - i - 1) ok = false;
if(!ok) {
used[j]--;
} else {
flag = true;
ansStr.push_back('a' + j);
last = pos;
break;
}
}
if(!flag) {
ans = -1;
break;
}
}
if(ans == -1) printf("-1\n");
else cout << ansStr << '\n';
}
return 0;
}