题目大意:
给你一个字符串,对于一个字符,如果左右两边的字符和这个不一样,那么就删除这个字符,一次可以删去多个字符,问最少的删除次数,使得不能再删。
题解:
这个是一个模拟题,写起来感觉挺恶心的,但是也很考验写题的技巧,用好的技巧写就显得很简洁。
按照网上的一个思路写的,首先证明一下这个解题思路:
假设有k个不同的块(每一块都是相同的),那么每次删除 (2*(k-1)) 个数,所以一共遍历 (frac{n}{2*(k-1)}) ,每次遍历的复杂度是 (O(k)) ,所以总的复杂度是 (k*frac{n}{2*(k-1)})
写法:
因为最后的形式是 (aaaabbbccababa) ,那么可以等价于 ([4a,3b,2c,1a,1b,1a,1b,1a])
然后很容易发现,每次操作如果是左右两边的块就 (-1) ,如果是中间的块就 (-2) ,所以每次操作完之后就可以重新合并一次,因为有些块删除之后,两边的块可以连在一起。
这个合并函数可以记录学习一下,以后碰到类似的可以使用这种方法:
void cal(){
int len = n;
n = 0;
for(int i=1;i<=len;i++){
if(e[i].num>0){
if(e[i].val==e[n].val) e[n].num += e[i].num;
else e[++n]=e[i];
}
}
}
(CODE:)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define debug(x) cout<<"debug:"<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
struct node{
int val,num;
node(int val=0,int num=0):val(val),num(num){}
}e[maxn];
char s[maxn];
int n;
void cal(){
int len = n;
n = 0;
for(int i=1;i<=len;i++){
if(e[i].num>0){
if(e[i].val==e[n].val) e[n].num += e[i].num;
else e[++n]=e[i];
}
}
}
void solve(){
int ans = 0;
while(n>1){
for(int i=2;i<n;i++) e[i].num -=2;
e[1].num--,e[n].num--;
ans++;
cal();
}
printf("%d
", ans);
}
int main(){
scanf("%s",s+1);
n = strlen(s+1);
e[0].val = -1;
for(int i=1;i<=n;i++) e[i].val = s[i]-'a',e[i].num = 1;
cal();
solve();
return 0;
}