模拟 [D. Colorful Points ]

模拟 D. Colorful Points

题目大意:

给你一个字符串,对于一个字符,如果左右两边的字符和这个不一样,那么就删除这个字符,一次可以删去多个字符,问最少的删除次数,使得不能再删。

题解:

这个是一个模拟题,写起来感觉挺恶心的,但是也很考验写题的技巧,用好的技巧写就显得很简洁。

按照网上的一个思路写的,首先证明一下这个解题思路:

假设有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;
}
原文地址:https://www.cnblogs.com/EchoZQN/p/13490849.html