题面
题目背景
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<l≤1000。
思路
啧,又是一道远古题。
看数据范围,直接上爆搜会 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 0aa
是回文串,直接返回 0 0 0QwQ
是回文串,直接返回 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;
}