664. Strange Printer (區間型 dp

Can see the screenshot explanation as well.

class Solution {
    public int strangePrinter(String s) {
        Integer[][] memo = new Integer[s.length()][s.length()];
        return dfs(s, 0, s.length()-1, memo);
    }
    private int dfs(String s, int i, int j, Integer[][] memo) {
        if (i == j) {
            return 1;
        }
        if (memo[i][j] != null) {
            return memo[i][j];
        }
        int minTurns = Integer.MAX_VALUE;
        for (int k = i; k < j; k++) {
            minTurns = Math.min(
                minTurns, 
                dfs(s, i, k, memo) + dfs(s, k+1, j,  memo)
            );
        }
        memo[i][j] = (s.charAt(i) == s.charAt(j) ? minTurns - 1 : minTurns);

        return memo[i][j];
    }
}

/**
This will seperate to all substring -> O(n^2)

Time Complexity Analysis
Subproblem Count:

The function helper(s, i, j, memo) is called for every possible substring s[i...j].
Since i can range from 0 to n-1 and j can range from i to n-1, 
the total number of unique subproblems is approximately the number of pairs (i, j) where i ≤ j.
There are
​n^2
such pairs, so the total number of subproblems is 
O(n^2).
Cost per Subproblem:

For each subproblem, the function attempts to split the string at every possible point k 
between i and j-1.
This involves checking up to j−i possible splits.
In the worst case, 
j−i can be as large as n−1.
Therefore, the time spent on each subproblem is 
O(n) 
Total Time Complexity:

Since there are 
O(n^2) subproblems, and each subproblem can take up to O(n) time to solve, 
the total time complexity is O(n^3)




thought

we want to find a break point to seperate to "2" part sub problem
keep do the dfs to sepeate to 2 part, until i == j (one char) so there is 1 turn 
ex: dfs(a) = 1 this is the base case

like
"aaabbb" -> to 
a aabbb 
or 
aa abbb 
or 
aaa bbb 
or
aaab bb
or 
aaabb b

see which one has the min turns

so we can record memo[i][j] which means from i to j the min turns in this substring

but notice, because when same char, we can print a seq, so like aaa -> only needs one turns, not 3 turns
so in every dfs(), we have to check if char[i] == char[j] -> then turns - 1

so dfs(aaa) = dfs(a) + dfs(aa) or dfs(aa) + dfs(a)
so dfs(aa) = dfs(a) + dfs(a) = 2 but char[i] == char[j], so dfs(aa) = 1
then dfs(aaa) = dfs(a) + dfs(aa) = 1+1 = 2, char[i] == char[j], so dfs(aaa) = 1
like a dfs 推論出來的結果! this part is really hard!


ex1:
i.j
abc

because if runs a for in k
min(
dfs(a) + dfs(bc) = 1 + 2
,
dfs(ab) + dfs(c) = 2 +1 
)

dfs(bc) -> dfs(b) + dfs(c)


dfs(a) dfs(b) = 1+1 = 2

ex2:
aba
2 + 1 -> but (s.charAt(i) == s.charAt(j)) -> so 3 -1 = 2

ex3:

aaabbb
= min(
dfs(a) + dfs(aabbb)
or
dfs(aa) + dfs(abbb)
or
dfs(aaa) + dfs(bbb) = 
min(
dfs(a) + dfs(aa) + dfs(b) + dfs(bb) -> (so dfs(aa = 1), because but i == j)
dfs(aaa) = 2 -> but i == j so 2 -1 again
vice versa, dfs(bbb) = 1. so best ans is dfs(aaa) + dfs(bbb) = 1 + 1 = 2
or
dfs(aa) + dfs(a) + dfs(bb) + dfs(b)
)
or 
dfs(aaab) + dfs(bb)
or
dfs(aaabb) + dfs(b)
)

dfs(aabbb) =
min...

so actually we can find in i to j, the best solution, so it's a interval dp

we can use memo[i][j] to record the min turns in i to j
 */

refer to:

https://leetcode.com/problems/strange-printer/discuss/152758/Clear-Logical-Thinking-with-Code

refer to:

https://leetcode-cn.com/problems/strange-printer/solution/qu-jian-dpzhuang-tai-fang-cheng-de-tui-d-gjnk/

https://leetcode-cn.com/problems/strange-printer/solution/kan-bu-dong-zhe-dao-ti-mu-de-ke-yi-xian-t067e/

DFS + memo

T: O(n* (n/2)+(n/2) * (...) => ?

so

dp

T: O(n^3)

S: O(n^2)

Last updated

Was this helpful?