蒟蒻第一次写题解。

题目传送门

题意概括

给定 $n$ 个字符串,求将其拼接后任意更改字符的情况下,转换为回文串的最小操作步数。

思路

首先不难发现,对于一个非回文串,如果要通过任意更改字符变成回文串,最小操作数量应当为左右两边字符不相同的数量。(后附正确性证明)

但是本题并没有直接给字符串 $s$,而是给了它的具体组成,因此应当从两边开始处理组成,若有字符余下,则不往里缩进,而是减少字符数量(题目中的 $a_i$)。

考虑双指针做法。左指针 $l$ 与右指针 $r$ 分别往内部缩进,超越时即为边界。

特别的,若两边数量恰好相等,两边都需要缩进。

最坏情况下答案约为 $\frac{1}{2}\sum a_i \le \frac{1}{2}\times 2\times10^5\times10^9=10^{14}$,超过 int 范围,需用 long long。

AC代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[200005];char c[200005];
int main(){
        ios::sync_with_stdio(0);
        cin.tie(nullptr);
    int n;long long ans=0;cin>>n;                 //答案可以达到10^14量级,注意long long
    for(int i=0;i<n;i++) cin>>a[i]>>c[i];
    int l=0,r=n-1;
    while(l<=r){                           //边界,l<r也可以,因为l==r时c[l]==c[r],不做任何处理
        if(a[l]<a[r]){                   //对左、右指针处数量的比较
            ans+=(c[l]==c[r]?0:a[l]);          //只有在字符不一致时才记录操作次数
            a[r]-=a[l];
            l++;                                        //左缩进
        }else if(a[r]<a[l]){
            ans+=(c[l]==c[r]?0:a[r]);
            a[l]-=a[r];
            r--;                                         //右缩进
        }else{                                             //a[l]==a[r]
            ans+=(c[l]==c[r]?0:a[r]);
            l++;r--;                                  //左右数量相等时需要同时缩进
        }
    }cout<<ans<<endl;
    return 0;
}

三目运算符可扩写为:

if(c[l]!=c[r]){
        ans+=a[l];   //或者a[r]
}

当然,本题的while循环内并不需要直接并列三个 if 语句,而是蒟蒻考场上这么写。

这里还有一种做法:

while(l<r){
    long long p=min(a[l],a[r]);
    a[l]-=p;a[r]-=p;
    ans+=(c[l]==c[r]?0:p);
    if(a[l]==0) l++;                    //等于0表示已经判断完,需要缩进
    if(a[r]==0) r--;
}

特别的,明显第二种做法更优。

正确性说明

回文串要求第 $i$ 个字符和第 $L+1−i$ 个字符相同。我们从两端向中间配对。

每一对配对的字符,如果相同则无需修改;如果不同,则至少需要修改其中一个。

由于每段内字符相同,左右两端各取 $min(a[l], a[r])$ 个字符进行配对,这些配对要么全部相同(即 $c_l=c_r$),要么全部不同(即 $c_l \neq c_r$)。不同的那些对,每对修改一个字符即可,因此贡献为 $min(a[l], a[r])$。

消去后继续处理剩余部分,直到所有位置配对完毕。

每一步的贡献都是下界,且可以同时达到,所以贪心最优。

时间复杂度

由于每个段只会被遍历 $1$ 次,且 $l$ 只增,$r$ 只减,因此时间复杂度为 $O(n)$。

需要存储 $a_i$ 与 $c_i$ 数组,因此空间复杂度为 $O(n)$。

AC记录