https://leetcode.com/problems/strange-printer/
给定一个长 n n n字符串 s s s,有一个刷子和一个长 n n n的白板,可以每次将任意位置开始的任意长度的段刷成同一个字符,先刷的会被后刷的覆盖。问要刷出 s s s至少要刷多少次。
首先,可以将 s s s去个重,即如果相邻位置字符相等,可以缩成一个,因为无论是什么方案,刷任意其中一个字符,都可以延伸着刷整个相同的一段。
设 f [ l ] [ r ] f[l][r] f[l][r]是 s [ l : r ] s[l:r] s[l:r]至少要刷多少次。那么当 l = r l=r l=r的时候, f [ l ] [ r ] = 1 f[l][r]=1 f[l][r]=1;考虑最后一次刷到 s [ l ] s[l] s[l]的那一次,假设这次刷的是 s [ l : k ] s[l:k] s[l:k],那么我们可以先将这次之前刷 s [ l : k ] s[l:k] s[l:k]这部分的操作都变为只刷 s [ k + 1 : ] s[k+1:] s[k+1:]这部分,并且将刷 s [ l : k ] s[l:k] s[l:k]的操作提前为第一次操作,这样显然所得方案的总操作次数不差于原来的方案。所以我们只需要考虑第一次操作就刷了 s [ l ] s[l] s[l]的所有方案。
枚举
k
k
k,当
k
=
l
k=l
k=l时,
f
[
l
]
[
r
]
=
1
+
f
[
l
+
1
]
[
r
]
f[l][r]=1+f[l+1][r]
f[l][r]=1+f[l+1][r];当
k
>
l
k>l
k>l时,如果
s
[
l
]
≠
s
[
k
]
s[l]\ne s[k]
s[l]=s[k],那么所有第一步刷
s
[
i
:
k
]
s[i:k]
s[i:k]的方案都可以转化为第一步刷
s
[
i
:
k
−
1
]
s[i:k-1]
s[i:k−1]的方案(因为
s
[
l
]
≠
s
[
k
]
s[l]\ne s[k]
s[l]=s[k],
s
[
k
]
s[k]
s[k]即使第一步刷了,之后也会覆盖,所以可以第一步刷
s
[
l
:
k
−
1
]
s[l:k-1]
s[l:k−1],操作数也一样),同理,如果
s
[
l
]
≠
s
[
k
−
1
]
s[l]\ne s[k-1]
s[l]=s[k−1],则可以转为先刷
s
[
l
:
k
−
2
]
s[l:k-2]
s[l:k−2]以此类推。所以所有的方案都可以转化为
s
[
l
]
=
s
[
k
]
s[l]=s[k]
s[l]=s[k]的方案,从而我们只需要枚举这种类型的方案即可。对于
s
[
l
]
=
s
[
k
]
s[l]=s[k]
s[l]=s[k]且第一步刷
s
[
l
:
k
]
s[l:k]
s[l:k]的方案来说,这种方案都可以对应
f
[
l
]
[
k
−
1
]
f[l][k-1]
f[l][k−1]的方案(第一步将
s
[
l
:
k
−
1
]
s[l:k-1]
s[l:k−1]刷成
s
[
l
]
s[l]
s[l],之后的所有操作步骤忽略
s
[
k
]
s[k]
s[k]即可),并且对于
f
[
l
]
[
k
−
1
]
f[l][k-1]
f[l][k−1]的最优方案,它也可以转化为刷
s
[
l
:
k
]
s[l:k]
s[l:k]的方案(第一步变为刷
s
[
l
:
k
]
s[l:k]
s[l:k]成
s
[
l
]
s[l]
s[l]即可,其余操作原样照做),所以此时
f
[
l
]
[
k
]
=
f
[
l
]
[
k
−
1
]
f[l][k]=f[l][k-1]
f[l][k]=f[l][k−1]。后面的部分最少操作次数即为
f
[
k
+
1
]
[
r
]
f[k+1][r]
f[k+1][r],所以此时
f
[
l
]
[
r
]
=
min
l
<
k
≤
r
{
f
[
l
]
[
k
−
1
]
+
f
[
k
+
1
]
[
r
]
}
f[l][r]=\min_{l
class Solution {
public:
int strangePrinter(string s) {
int n = 0;
// 去重
for (int i = 0; i < s.size(); i++)
if (!n || s[i] != s[n - 1]) s[n++] = s[i];
s.resize(n);
int f[n][n];
for (int len = 1; len <= n; len++)
for (int l = 0; l + len - 1 < n; l++) {
int r = l + len - 1;
if (len == 1) f[l][r] = 1;
else {
f[l][r] = 1 + f[l + 1][r];
for (int i = l + 1; i <= r; i++)
if (s[i] == s[l])
f[l][r] = min(f[l][r], f[l][i - 1] + (i + 1 <= r ? f[i + 1][r] : 0));
}
}
return f[0][n - 1];
}
};
时间复杂度 O ( n 3 ) O(n^3) O(n3),空间 O ( n 2 ) O(n^2) O(n2)。