[NOIP模擬賽]字母選擇問題

題目描述
輸出僅包含小寫英文字符的字符串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;
}


發佈了101 篇原創文章 · 獲贊 7 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章