回文相关

考虑对于一个确定的字符串,判断其是否回文,可以

直接hash,那么多事干嘛

那么对于一个非回文的字符串,欲使其回文,求使其回文付出的最小代价?

可以删,可以增?

LuoguP1435 (没给出长度)

LuoguT16647 (已经给出长度)

本题中,仅支持

  • 插入

最开始打了个搜索,非常垃圾并没有搜对。

正解是DP。

一个回文串[l,r]中[l+1,r-1]一定也是回文串,长的回文可以由短的回文推出

f[i][j] 表示使[i,j]成为回文串付出的代价。

f[i][j] = ( f[i+1][j-1] ) ( s[i]==s[j] )

f[i][j] = min ( f[i+1][j] , f[i][j-1] ) ( s[i]!=s[j] )

接下来考虑如何枚举 i j ,我们可以依次枚举 回文长度len 左端点 i ,再由leni计算出j

#include<iostream>
#include<cstring>
#include<string>
using namespace std;
#define j i+len-1//计算右端点

int n;
string s;
int f[5002][5002];

int main()
{
    ios::sync_with_stdio(0);
    cin>>n>>s;
    s=" "+s;
    for(int i=1;i<n;i++)
        f[i][i+1]=(s[i]==s[i+1]?0:1);//预处理,一个字符和它后面的字符不同,那么这两个字符回文的代价就为1
    for(int len=3;len<=n;len++)//枚举长度
        for(int i=1;i<=n-len+1;i++)//枚举起点
            if(s[i]==s[j])
                f[i][j]=f[i+1][j-1];
            else
                f[i][j]=min(f[i+1][j],f[i][j-1])+1;
    cout<<f[1][n];
    return 0;
}

或者直接枚举i j

#include<iostream>
#include<cstring>
#include<string>
using namespace std;

int n;
string s;
int f[5002][5002];

int main()
{
	ios::sync_with_stdio(0);
	cin>>n>>s;
	s=" "+s;
	for(int i=1;i<n;i++)
		f[i][i+1]=(s[i]==s[i+1]?0:1);
	/*for(int i=n;i;i--)
		for(int j=i+1;j<=n;j++)这样也是可以的*/
  	for(int j=2;j<=n;j++)
		for(int i=j-1;i;i--)
			if(s[i]==s[j])
				f[i][j]=f[i+1][j-1];
			else
				f[i][j]=min(f[i+1][j],f[i][j-1])+1;
	cout<<f[1][n];
	return 0;
}

注意,这里枚举不能

for(int i=1;i<=n;i++)
  for(int j=i+1;j<=n;j++)

因为i j的状态需要从i+1 i j-1 j得来,必须从中间向两边枚举


考虑增加操作

  • 增加
  • 删除

每种、每次操作又都有不同的代价。考虑代价最小?

LuoguP2890

字串S长M,由N个小写字母构成。欲通过增删字母将其变为回文串,增删特定字母花费不同,求最小花费。

其实和上面还是一样,但是在转移时有了更多的选择

s[i]与s[j]相同,直接从i+1 j-1转移过来

从i j-1转移过来,取添加或删除字符j的最小代价

从i+1 j转移过来,取添加或删除字符i的最小代价

关于i j的枚举,仍可以枚举长度、i 再算出j

或者直接枚举i j

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int add[200],del[200],f[2001][2001];//add[]增加字符的代价,del[]删除字符的代价
int main()
{
    ios::sync_with_stdio(false);
    int n,m;
    cin>>n>>m;
    string s;
    cin>>s;
    s=' '+s;
    for(int i=0;i<n;i++)
    {
        char c;
        cin>>c;
        cin>>add[(int)c]>>del[(int)c];
    }
    for(int r=2;r<=m;r++)
        for(int l=r-1;l;l--)
        {
            if(s[l]==s[r])
                f[l][r]=f[l+1][r-1];
            else//这里也可以改一下然后把括号去掉
            {
                int a1=f[l][r-1]+min(del[(int)s[r]],add[(int)s[r]]);
                int a2=f[l+1][r]+min(del[(int)s[l]],add[(int)s[l]]);
                f[l][r]=min(a1,a2);
            }
        }
    cout<<f[1][m];
    return 0;
}

我写这么艰难还是因为我不会DP

posted @ 2017-11-08 19:47  syhien  阅读(131)  评论(0编辑  收藏  举报