[NOIP模拟赛]字母选择问题

30 篇文章 0 订阅
2 篇文章 0 订阅
题目描述
输出仅包含小写英文字符的字符串S,支持以下一种操作:选择一个字母X,然后选定该字符串中所有的X,并用另一种小写英文字母Y替换它们,代价为 被替换的字符的个数

例如,S=“goose”,本次操作所选的X=“o”且Y=“e”,操作结束后的S=“geese”,本次操作的代价为2。要求只用这种操作,使得该字符串变为回文串,同时使得代价之和最小。


输入格式

第1行:1个字符串S(1≤S。Size()≤10^5)


输出格式

第1行:1个整数,表示把S改为回文串的最小操作代价


输入样例

geese


输出样例

2


样例说明
第1次操作把g改为e,第2次操作把s改为e,字符串变成:“eeeee”,即为回文串。

 


题解
对称位置上的字符,若它们不相同,那么修改后必须相同。
由此建一个最多有26个点的图,每个点表示一个小写字母。若对称位置上的字符不相同,则将两个点连起来。最后会有一个或多个联通块,一个联通块中的字符必须相同。因此对于每个联通块,选择数量最多的字母,将其他字母全部变成这个字母,代价为其他字母数量之和。最后将所有联通块加起来就是答案。
建图可以直接建,也可以用并查集维护。这里选择直接建。


#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;

int sum, cnt[30];
bool map[30][30];
char ch[N];
int Num( char s ) { return s-'a'+1; }

int fir[30], ecnt;
struct node{ int e, next; } edge[2000];
void Link( int s, int e ) {
	edge[++ecnt].e=e; edge[ecnt].next=fir[s]; fir[s]=ecnt;
	edge[++ecnt].e=s; edge[ecnt].next=fir[e]; fir[e]=ecnt;
}

int que[30];
void BFS( int s ) {
	int l=1, r=0, maxv=0;
	que[++r]=s;
	maxv=max( maxv,cnt[s] );
	sum+=cnt[s]; cnt[s]=0;
	while( l<=r ) {
		s=que[l++];
		for( int i=fir[s]; i; i=edge[i].next )
			if( cnt[ edge[i].e ] ) {
				que[++r]=edge[i].e;
				maxv=max( maxv,cnt[ edge[i].e ] );
				sum+=cnt[ edge[i].e ]; cnt[ edge[i].e ]=0;
			}
	}
	sum-=maxv;
}

int main() {
	scanf( "%s", ch+1 );
	int len=strlen( ch+1 );
	for( int l=1, r=len; l<=r; l++, r-- ) {
		int c1=Num(ch[l]), c2=Num(ch[r]);
		cnt[c1]++; cnt[c2]++;
		if( l==r ) { cnt[c1]--; break; }
		if( ch[l]!=ch[r] && !map[c1][c2] )
			Link( c1, c2 ), map[c1][c2]=1;
	}
	for( int i=1; i<=26; i++ )
		if( cnt[i] ) BFS(i);
	printf( "%d\n", sum );
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值