PepperLa’s String
题意 :
给一个字符串 s ss,可以删去一个字符,并且可以对其进行任意的压缩表示一个字符加其连续出现次数的十六进制表示,求最短的表示串,相同长度则输出字典序最小的。
思路 :
如果连续出现两个或以上的字符,压缩表示长度更短,如果只有一个,则显然不需要压缩。考虑删去一个字符使得长度减小的情况:① 删去单一字符,长度减一;② 删去两个字符中的一个,长度减一;③ 删去一个字符后,其出现次数位数减少一。从左到右扫描字符串,如果有 ① 情况且字典序能变小,直接停止;否则剩余能减小长度的情况都会增大字典序(除了在结尾处删),则尽可能让删除位置靠右。不能减小长度的情况,则删去第一个字符最优。
AC代码 :
#include <iostream>
#include <map>
#include <utility>
#include <cstring>
#include <algorithm>
#include <math.h>
#define IOS ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 1e6 + 5;
string s, ch("0123456789ABCDEF");
struct Node{
char ch; string v; int cnt;
} a[maxn];
int n, m;
string getS(int x){
string s = "";
while(x > 0){
s += ch[x % 16];
x /= 16;
}
reverse(s.begin(), s.end());
return s;
};
void print(){
for(int i = 0; i < m; ++i){
if(!a[i].cnt) continue;
else if(a[i].cnt == 1) cout << a[i].ch;
else cout << a[i].ch << a[i].v;
}
cout << "\n";
}
void solve(){
int ret = 0;
for(int i = 0; i < m; ++i){
if(a[i].cnt == 1){
ret = i;
if(i < m && a[i + 1].ch < a[i].ch) break;
}
else if(a[i].cnt == 2) ret = i;
else if(a[i].v == "1" + string(a[i].v.size() - 1, '0')) ret = i;
}
--a[ret].cnt, a[ret].v = getS(a[ret].cnt);
print();
}
int main(){
ios::sync_with_stdio(0); cin.tie(0);
while(cin >> s){
n = s.size(), m = 0;
int cnt = 1, flg = 0;
for(int i = 1; i < n; ++i){
if(s[i] == s[i - 1]) ++cnt;
else a[m++] = Node{s[i - 1], getS(cnt), cnt}, cnt = 1;
}
a[m++] = Node{s[n - 1], getS(cnt), cnt};
solve();
}
return 0;
}