题目描述
输出仅包含小写英文字符的字符串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; }
[NOIP模拟赛]字母选择问题
最新推荐文章于 2023-02-22 11:33:30 发布