leetcode 664. Strange Printer

题目
There is a strange printer with the following two special requirements:

The printer can only print a sequence of the same character each time.
At each turn, the printer can print new characters starting from and ending at any places, and will cover the original existing characters.
Given a string consists of lower English letters only, your job is to count the minimum number of turns the printer needed in order to print it.
这题乍一看,没弄明白是什么意思,后来去别人博客里瞄了一眼,恍然大悟(我发誓没看他代码)。
这题是说,每一轮次打印机可以连着打印相同的字母,然后下一轮次可以在上一轮次打出的字母基础上挑个位置开始连着打印新的字母,新轮次覆盖旧轮次的。直到打印出题目要求的字符串为之。
我一开始的思维是,用递归。头尾字符相同的话,可以去掉,然后返回剩下的字符串需要的打印次数再+1,如果头尾字符不同的话,可以尝试去掉左侧的,或者右侧的,挑最小值返回,即每层递归会有一到二个路口可选择,最坏的情况,遍历到底,复杂度可能会达到2^100。。。。。(我的I5处理器已经跑了20分钟了)。很显然这是个错误的思路,但容我恬不知耻地把代码粘出来(不能浪费智力成果啊):

int countTimes(string &s, int b, int e) {
    static int ttt = 0;
    cout << ++ttt << endl;
    if (b>e)
        return 0;
    if (b == e)
        return 1;
    char key = s[b];
    int oriB = b;
    int oriE = e;
    if (s[b] == s[e]) {
        while (b <= e && s[b] == key) {
            ++b;
        }
        while (b <= e && s[e] == key) {
            --e;
        }
        return 1 + countTimes(s, b, e);
    }
    else {
        while (b<oriE && s[b] == s[b + 1])
            ++b;
        while (oriB<e && s[e] == s[e - 1])
            --e;
        return 1 + min(countTimes(s, b + 1, oriE), countTimes(s, oriB, e - 1));
    }
}
int strangePrinter(string s) {
    if (s.size() == 0)
        return 0;

    return countTimes(s, 0, s.size() - 1);
}

后来去网上借鉴了一下别人的思路,除了DFS,还要用memoization技术,才能避免不必要的重复计算。因此诞生了新想法:二维数组动态规划法。
下方代码还是比较好理解的,唯一需要解释的地方在于:
dp[s][e] = min(dp[s][e], helper(str,dp,l,i)+helper(str,dp,i,r))
只有str[i]==str[s]的地方,才有可能借用中间路径实现优化。

class Solution {
public:
    int strangePrinter(string s) {
        int n = s.size();
        vector<vector<int>> dp(n+1, vector<int>(n+1, 0));
        return helper(s, dp, 0, n);
    }
private:
    int helper(string& str, vector<vector<int>>& dp, int s, int e) {
        if (s >= e) return 0;
        if (dp[s][e]) return dp[s][e];
        // 处理 str[s] 的首尾字符
        // 注意范围是左闭右开 [s,e) [l,r)
        int l = s, r = e;
        while (l < e && str[l] == str[s]) l++;
        while (r > l && str[r-1] == str[s]) r--;
        dp[s][e] = 1+helper(str, dp, l, r);
        for (int i = l; i < r; i++) {
            if (str[i] == str[s]) {
                dp[s][e] = min(dp[s][e], helper(str,dp,l,i)+helper(str,dp,i,r));
                while (i < e && str[i] == str[s]) i++;
            }   
        }
        return dp[s][e];
    }
};

猜你喜欢

转载自blog.csdn.net/qq_36946274/article/details/80961423