题意
有两个由小写英文字母组成的等长字符串A和B。你可以一次性将一个字符串的一个子串中的字符全部刷成任何你想要同一字符。求把字符串A刷成B的最少次数。
分析
这一题非常像 [CQOI2007] 涂色 ,准确说是加强版
但是简单考虑直接将A刷成B,由于AB两串中字符的不确定性(有些位置上字符相同而有些不同),状态转移方程比较难推出,所以可以换一种思路来简化问题
我们可以分两步DP处理,先求出将空白串刷成B串中任意子串的次数,再借此得出A刷成B的结果
定义二维数组f[i][j]表示将空白串的i到j位置刷成B串的i到j位置的最少次数
当 $i=j$ 时,$f[i][j]=1$
当 $i \neq j$ 时,
$$
{\large f[i][j]=
\begin{cases}
min(f[i+1][j],f[i][j-1]) & {B[i]=B[j]} \\
min(f[i][j],f[i][k]+f[k+1][j])(i \leq k < j) & {B[i] \neq B[j]}
\end{cases}}
$$
显然需要区间DP,不断扩展枚举子串的长度,并且要枚举子串中间断点k
然后定义d[i]表示将A串前i个字符刷成B串前i个字符的最少次数,最初 $d[i]=f[1][i]$
$$
{\large d[i]=
\begin{cases}
min(d[i],d[i-1]) & {A[i]=B[j]} \\
min(d[i],d[k]+f[k+1][i])(i \leq k < j) & {A[i] \neq B[j]}
\end{cases}}
$$
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <vector> #include <cmath> using namespace std; #define ll long long #define inf 0x7fffffff #define N 105 int n; char a[N], b[N]; int f[N][N], d[N]; int main() { while (scanf("%s%s", a + 1, b + 1) == 2) { memset(f, 0x3f, sizeof f); n = strlen(a + 1); for (int i = 1; i <= n; i++) f[i][i] = 1; for (int l = 2; l <= n; l++) for (int i = 1; i + l - 1 <= n; i++) { int j = i + l - 1; if (b[i] == b[j]) f[i][j] = min(f[i + 1][j], f[i][j - 1]); else for (int k = i; k < j; k++) f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j]); } for (int i = 1; i <= n; i++) { d[i] = f[1][i]; if (a[i] == b[i]) d[i] = min(d[i], d[i - 1]); else for (int k = 1; k < i; k++) d[i] = min(d[i], d[k] + f[k + 1][i]); } printf("%d\n", d[n]); } return 0; }