『 [IOI2000] 回文字串 』你意料之外的记忆化题解!

题目传送门

题面

题目背景

IOI2000 第一题

题目描述

回文词是一种对称的字符串。任意给定一个字符串,通过插入若干字符,都可以变成回文词。此题的任务是,求出将给定字符串变成回文词所需要插入的最少字符数。

比如 Ab3bd \verb!Ab3bd! Ab3bd 插入 2 2 2 个字符后可以变成回文词 dAb3bAd \verb!dAb3bAd! dAb3bAd Adb3bdA \verb!Adb3bdA! Adb3bdA,但是插入少于 2 2 2 个的字符无法变成回文词。

注意:此问题区分大小写。

输入格式

输入共一行,一个字符串。

输出格式

有且只有一个整数,即最少插入字符数。

样例 #1

样例输入 #1

Ab3bd

样例输出 #1

2

提示

数据范围及约定

记字符串长度为 l l l

对于全部数据, 0 < l ≤ 1000 0<l\le 1000 0<l1000

思路

啧,又是一道远古题。

看数据范围,直接上爆搜会 TLE ⁡ \operatorname{TLE} TLE,其时间复杂度达到了 O ( 3 1000 ) O(3^{1000}) O(31000),显然需要优化。

众所周知,动态规划题你要是闲的 dfs ⁡ \operatorname{dfs} dfs,一般都得用上记忆化(记忆化搜索可以看作是动态规划的一种实现技巧,与动态规划中存储子问题解的思想相似)。我们可以定义一个二维数组 f [   ] [   ] f[\ ][\ ] f[ ][ ],设字符串为 s s s f i , j f_{i,j} fi,j 表示从 s i s_i si s j s_j sj 需要插入多少个字符才能使得它是一个回文串。

好了,接下来是深搜状态的转移:如果 s i = s j s_i = s_j si=sj,那么不需要加任何东西,直接看里面一层 dfs(i,j) ⁡ = dfs(i+1,j-1) ⁡ \operatorname{dfs(i,j)} = \operatorname{dfs(i+1,j-1)} dfs(i,j)=dfs(i+1,j-1);否则,可以选择把左边的补上,把右边的补上,两边都补,根据题意取最小值即可,详见代码。

特殊条件:

  • 记忆化优化,直接返回 f i , j f_{i,j} fi,j
  • A是回文串,直接返回 0 0 0
  • aa是回文串,直接返回 0 0 0
  • QwQ是回文串,直接返回 0 0 0

AC Code

#include <bits/stdc++.h>
using namespace std;
string s;
int n;
int f[1005][1005];
int dfs(int l,int r)
{
	if(l > r) return 1145141919;
	
	if(r - l == 1 && s[l] == s[r]) return 0;
	if(r - l == 2 && s[l] == s[r]) return 0;
	if(f[l][r] != 0x7f7f7f7f) return f[l][r];
	int ret = 0;
	
	if(s[r] == s[l]) ret = dfs(l+1,r-1);
	else ret = min(dfs(l+1,r-1) + 2,min(dfs(l+1,r) + 1,dfs(l,r-1) + 1));
	f[l][r] = ret;
	return ret;
}
int main()
{	
	memset(f,0x7f7f7f7f,sizeof(f));
    cin >> s;
    n = s.size();
    for(int i = 0;i < n;i++) f[i][i] = 0;
    cout << dfs(0,n-1);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值