664. Strange Printer

https://leetcode.com/problems/strange-printer/

DP问题最重要的就是状态转移方程。这题我最开始考虑的状态转移方程如下:

这样的时间复杂度是O(n^2 * 26 * n),空间复杂度是O(n^2 * 26). 用Python实现是超时的,但是使用Java实现没有问题。这个我在后面附上第一种解法。


事实证明是我想复杂了,讨论区里面有人给出了解法

第一种:dp[i][j] = 1 + dp[i + 1][j].
i单独打印, s[i + 1, j]段另外打印

第二种:dp[i][j] = min(dp[i][j], dp[i + 1][k] + dp[k + 1][j]);
dp[i + 1][k]代表将i放到[i+ 1, k]一起打印,dp[k + 1][j]代表[k + 1, j]另外打印,(s[i] == s[k])

第三种:dp[i][j] = min(dp[i][j], dp[i + 1][j]);
dp[i + 1][j]代表将i放入[j + 1, i]一起打印(s[i] == s[j])

其实case3是case2的特殊情况,所以其实只要考虑两种。我觉得case2里面这点很重要 "将i放到[i+ 1, k]一起打印",这可以理解成某种逆向思维的解法: "因为s[k]==s[i],所以其实可以在某次打印s[k]的时候,顺带把s[i]打印了。"不过其实操作应该是先以s[i]打印s[i..k]这段。

这种逆向思维解法有个好处是,将打印计算的成本后移了,避免了去考虑"上次打印的字符"是什么这个问题。如果我们之前的考虑是"先以s[i]打印s[i..k]"这段的话, 那么很容易会写出代价是 1 + s[i+1..k-1], 不过这个是错误的,因为在s[i+1..k-1]里面还需要考虑上次打印的字符是什么。

这题状态转移方程有点奇怪,后面可以在继续考虑考虑。另外在解决问题之前,可以先对字符串做压缩将相同字符使用一个表示,比如"aaaabbbbc" -> "abc".

class Solution:
    def strangePrinter(self, s: str) -> int:
        if not s:
            return 0

        p = s[0]
        ss = []
        for c in s:
            if c != p:
                ss.append(p)
                p = c
        ss.append(p)
        print(''.join(ss))

        import functools

        @functools.lru_cache(None)
        def fun(i, j):
            if i > j:
                return 0

            ans = 1 + fun(i + 1, j)
            for k in range(i + 1, j + 1):
                if ss[i] == ss[k]:
                    ans = min(ans, fun(i + 1, k) + fun(k + 1, j))
            return ans

        ans = fun(0, len(ss) - 1)
        return ans

然后我在粘贴下第一种解法

import java.util.*;

class Solution {
    int[][][] dp;

    public int query(char[] cs, int i, int j, int c) {
        if (i > j) {
            return 0;
        }
        int ans = dp[i][j][c];
        if (ans != -1) {
            return ans;
        }
        ans = 1 << 30;
        int k = i;
        while ((k <= j) && (cs[k] == cs[i])) {
            k += 1;
        }
        if (cs[i] == c) {
            ans = query(cs, k, j, c);
        } else {
            for (int k2 = k; k2 <= j + 1; k2++) {
                int res = 1 + query(cs, k, k2 - 1, cs[i]) + query(cs, k2, j, c);
                ans = Math.min(ans, res);
            }
        }
        dp[i][j][c] = ans;
        return ans;
    }

    public int strangePrinter(String s) {
        int n = s.length();
        if (n == 0) {
            return 0;
        }
        char[] cs = s.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            cs[i] -= 'a';
        }
        dp = new int[n][n][26];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                for (int c = 0; c < 26; c++) {
                    dp[i][j][c] = -1;
                }
            }
        }
        int ans = query(cs, 0, n - 1, cs[0]) + 1;
        return ans;
    }
}