P5041 [HAOI2009]求回文串

题目链接

P5041 [HAOI2009]求回文串

题目描述

所谓回文串,就是对于给定的字符串,正着读和反着读都一样,比如ABCBA就是一个回文串,ABCAB则不是。我们的目标是对于任意输入的字符串,不断将第i个字符和第i+1个字符交换,使得该串最终变为回文串。求最少交换次数。

输入格式

一个由大写字母字母组成的字符串。

输出格式

若能经过有限次操作能将原串变为回文串,则输出最少操作次数;否则输出-1。

输入输出样例

输入

SHLLZSHZS

输出

4

说明/提示

样例说明

交换 L 和 Z 变成 SHLZLSHZS
交换 L 和 Z 变成 SHZLLSHZS
交换 L 和 S 变成 SHZLSLHZS
交换 H 和 Z 变成 SHZLSLZHS

数据范围

40% 的数据, 长度50000

100% 的数据, 长度106

解题思路

贪心,逆序对

贪心策略:每次选择最靠外边的字符作为回文串的一部分
证明(摘自 TsReaper ):
构造回文串的过程,实际上是每次选择一对字母并把它们交换到字符串头尾的过程。考虑字母 x 和字母 y 哪 个先选,分以下情况讨论:

  • 字母 xy 的位置满足 a个字母 xb 个字母 yc 个字母 yd 个字母 xe个字母  。如果先把 x 换到头尾,再把 y 换到头 尾,那么需要 (a+e)+(b+d+a+e) 次交换;如果先换 y 再换 x ,那么需要 (a+b+1+d+e+1)+(a+e) 次交换。显然先换 x 更优。

  • 字母 xy 的位置满足a个字母 xb 个字母 yc 个字母 xd 个字母 ye个字母  如果先换 x 再换 y ,那么需要 (a+d+ e+1)+(a+b+e) 次交换;如果先换 y 再换 x ,那么需要 (a+b+1+e)+(a+d+e) 次交换。先换哪个都一样。

  • 字母 xy 的位置满足a个字母 xb 个字母 xc 个字母 yd 个字母 ye个字母 。如果先交换 x 再交换 y,那么需要(a+c+d+e+2)+(a+b+c+e) 次交换;如果先换 y 再换 x ,那么需要 (a+b+c+2+e)+(a+c+d+e) 次交换。先换哪个都一样。
    上述讨论可以得到结论:每次交换最外边出现的字母不劣。因此贪心解法成立。
    据此我们求出这样的数组:a[i] 表示现在回文串中的第 i 个字符在原字符串中出现的位置为 a[i],即要使该序列有序需要交换相邻项的最少操作次数,即根据冒泡排序,答案即为逆序对个数,可通过树状数组求出

  • 时间复杂度:O(nlogn)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e6+5; string s; int n,res[N],b[N],c[N]; vector<int> a[26]; bool v[N]; void add(int x,int y) { for(;x<=n;x+=x&-x)c[x]+=y; } int ask(int x) { int res=0; for(;x;x-=x&-x)res+=c[x]; return res; } int main() { cin>>s; n=s.size(); s=' '+s; for(int i=1;i<=n;i++) { b[i]=s[i]-'A'; a[s[i]-'A'].pb(i); } int f=0; for(int i=0;i<26;i++) if(a[i].size()&1)f++; if(n%2==0&&f||n%2==1&&f!=1)puts("-1"); else { if(n&1) { for(int i=0;i<26;i++) if(a[i].size()&1) { res[n/2+1]=a[i][a[i].size()/2]; v[a[i][a[i].size()/2]]=true; } } int l=1,r=n,pos=1; while(pos<=n) { if(v[pos]) { pos++; continue; } if(pos>n||l>r)break; v[pos]=true; res[l++]=pos++; int t=a[b[pos-1]][a[b[pos-1]].size()-1]; v[t]=true; res[r--]=t; a[b[pos-1]].pop_back(); } } LL ret=0; for(int i=n;i;i--)ret+=ask(res[i]),add(res[i],1); cout<<ret; return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16034907.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(55)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示