题目地址:
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<k\le r}\{f[l][k-1]+f[k+1][r]\} f[l][r]=l<k≤rmin{f[l][k−1]+f[k+1][r]}当然 k = r k=r k=r的时候取 f [ k + 1 ] [ r ] = 0 f[k+1][r]=0 f[k+1][r]=0。代码如下:
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)。