CF137D Palindromes 题解

题意

一个字符串,求最少改多少个字符能把这个字符串拆成 \(k\) 个回文串。

分析

先暴力求出每一段改为回文串所需的最小修改次数,将 \([i,j]\) 改为回文串所需的最小修改次数为 \(f_{i,j}\) ,然后 dp 即可。定义 \(dp_{i,k}\) 为将 \([0,i]\) 改为能拆成 \(k\) 个回文串所需的最小修改次数,\(dp_{i,k}=\min(dp_{j \in [0,i-1],k})\)。在 dp 的时候,更新 dp 值的同时,记录路径,\(dp1_{i,k}\) 表示 \(dp_{i,k}\) 是从哪转移的,最后从后向前找到路径,依照路径分段输出,再修改一下回文即可。

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
const int inf=2139062143;
inline void qread(){}template<class T1,class ...T2>
inline void qread(T1 &a,T2&... b)
{
	register T1 x=0;register bool f=false;char ch=getchar();
	while(ch<'0') f|=(ch=='-'),ch=getchar();
	while(ch>='0') x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	x=(f?-x:x);a=x;qread(b...);
}
template<class T1,class T2>inline T1 qmax(const T1 &x,const T2 &y){return x>y?x:y;}
template<class T1,class T2>inline T1 qmin(const T1 &x,const T2 &y){return x<y?x:y;}
const int MAXN=507;
string s;int n,m,dp[MAXN][MAXN],f[MAXN][MAXN],ans=inf,dp1[MAXN][MAXN];
vector<int>a;
int main()
{
	cin>>s;n=s.size();qread(m);int i,j,k;mem(dp,0x7f);
	for(i=0;i<n;i++)
	{
		for(j=i+1;j<n;j++)
		{
			int l=j-i+1;
			for(k=i;k<i+l/2;k++) if(s[k]!=s[j-(k-i)]) f[i][j]++; //将[i,j]变成回文串的最小花费
		}
	}
	for(i=0;i<n;i++) dp[i][1]=f[0][i];
	for(i=0;i<n;i++) //当前枚举到的位数
	{
		for(j=0;j<i;j++)
		{
			for(k=1;k<=m;k++) //分成的回文串数
			{
				if(dp[i][k]>dp[j][k-1]+f[j+1][i])
				{
					dp[i][k]=dp[j][k-1]+f[j+1][i]; //更新dp值
					dp1[i][k]=j;
				}
			}
		}
	}int pos=n-1,t=0;
	for(i=1;i<=m;i++)
		if(ans>dp[n-1][i]) ans=dp[n-1][i],t=i; //找出dp最小值
	while(pos>=0&&t>=0)
	{
		a.push_back(pos); //加入路径
		pos=dp1[pos][t--];
	}
	a.pop_back();
	reverse(a.begin(),a.end()); //由于是从后往前加入的,所以需要翻转
	printf("%d\n",ans);
	int last=-1;
	for(i=0;i<a.size();i++)
	{
		if(i!=0) putchar('+');
		for(j=last+1;j<=a[i];j++) 
		{
			if(s[j]==s[a[i]-(j-last-1)]) putchar(s[j]); //无需修改
			else if(a[i]-(j-last-1)>j) putchar(s[j]); //前半部分对应的已修改
			else putchar(s[a[i]-(j-last-1)]); //需要修改
		}
		last=a[i];
	}
	return 0;
}
posted @ 2022-02-17 19:47  l_x_y  阅读(26)  评论(0编辑  收藏  举报