---
# System prepended metadata

title: Weekly Contest 347
tags: [Weekly Contest]

---

###### tags: `Weekly Contest`
# Weekly Contest 347

## [2710. Remove Trailing Zeros From a String](https://leetcode.com/problems/remove-trailing-zeros-from-a-string/)(<font color=#00B8A3>Easy</font>)

限制 :

<ul>
<li><code>1 &lt;= num.length &lt;= 1000</code></li>
<li><code>num</code> consists of only digits.</li>
<li><code>num</code> doesn't have any leading zeros.</li>
</ul>

### Solution
照題意做，移除後綴0即可。
#### 時間複雜度: $O(n)$

#### 空間複雜度: $O(1)$

程式碼:

```c++=
class Solution {
public:
    string removeTrailingZeros(string num) {
        for(int i=num.size()-1;i>=0;i--){
            if(num[i] == '0')
                num.pop_back();
            else break;
        }
        return num;
    }
};
```


## [2711. Difference of Number of Distinct Values on Diagonals](https://leetcode.com/problems/difference-of-number-of-distinct-values-on-diagonals/)(<font color=#00B8A3>Easy</font>)

限制 :

<ul>
<li><code>m == grid.length</code></li>
<li><code>n == grid[i].length</code></li>
<li><code>1 &lt;= m, n, grid[i][j] &lt;= 50</code></li>
</ul>

### Solution
這題難點應該是看懂題目，看懂題目後，用set去統計不同元素有幾個之後，即可算出ans。
> p.s.在n, m很大且grid[i][j]的範圍很小時，可以考慮用counting sort將時間複雜度壓成$O(n*m*min(n, m)*range(grid[i][j]))$
#### 時間複雜度: $O(n*m*min(n, m)*log(min(n, m)))$

#### 空間複雜度: $O(min(n, m))$

程式碼:

```c++=
class Solution {
public:
    vector<vector<int>> differenceOfDistinctValues(vector<vector<int>>& grid) {
        int n=grid.size(), m=grid[0].size();
        vector<vector<int> > ans(n, vector<int>(m));
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                set<int> si;
                int nowi = i+1, nowj = j+1, sum;
                while(nowi<n&&nowj<m){
                    si.insert(grid[nowi][nowj]);
                    nowi++, nowj++;
                }
                sum = si.size();
                
                si.clear();
                nowi = i-1, nowj = j-1;
                while(nowi>=0&&nowj>=0){
                    si.insert(grid[nowi][nowj]);
                    nowi--, nowj--;
                }
                sum -= si.size();
                sum = abs(sum);
                
                ans[i][j] = sum;
            }
        }
        
        return ans;
    }
};
```


## [2712. Minimum Cost to Make All Characters Equal](https://leetcode.com/problems/minimum-cost-to-make-all-characters-equal/)(<font color=#FFC011>Medium</font>)

限制 :

<ul>
<li><code>1 &lt;= s.length == n &lt;= 10<sup>5</sup></code></li>
<li><code>s[i]</code> is either <code>'0'</code> or <code>'1'</code></li>
</ul>

### Solution
使用複雜的dp解，令$dp[i][j] 等於將前i個字元全部轉成j所需要的cost$，由此可以導出dp式是:

$\begin{cases}
dp[i][0] = min(dp[i-1][1]+i, dp[i-1][0]),\qquad\qquad\qquad\qquad\quad  \text{if}\quad s[i]=\text{'0'} \\
dp[i][1] = min(dp[i-1][1]+i+i+1, dp[i-1][0]+i+1),\qquad\ \  \text{if}\quad s[i]=\text{'0'} \\
\end{cases}$

$\begin{cases}
dp[i][0] = min(dp[i-1][1]+i+1, dp[i-1][0]+i+i+1),\qquad\ \  \text{if}\quad s[i]=\text{'1'} \\
dp[i][1] = min(dp[i-1][1], dp[i-1][0]+i),\qquad\qquad\qquad\qquad\quad \text{if}\quad s[i]=\text{'1'} \\
\end{cases}$

因為這個dp式只考慮到第一個操作，但題目是有第二個操作的，所以需要再建立第二個dp array存取只考慮第二個操作的結果。

$dp1[i][j]的意義是將前i個字元全部轉成j所需要的cost，dp2[i][j]的意義是將第i+1個字元後全部轉成j所需要的cost。$
所以答案就是:
$ans = min\{\ min(dp1[i][0]+dp2[i][0], dp1[i][1]+dp2[i][1])\ \}$
#### 時間複雜度: $O(n)$

#### 空間複雜度: $O(n)$

程式碼:

```c++=
class Solution {
public:
    long long minimumCost(string s) {
        long long dp1[100005][2] = {0};
        long long dp2[100005][2] = {0};
        
        for(int i=1;i<=s.size();i++){
            if(s[i-1] == '1'){
                dp1[i][1] = min(dp1[i-1][1], dp1[i-1][0]+i-1);
                dp1[i][0] = min(dp1[i-1][0]+i+i-1, dp1[i-1][1]+i);
            }
            else {
                dp1[i][1] = min(dp1[i-1][1]+i-1+i, dp1[i-1][0]+i);
                dp1[i][0] = min(dp1[i-1][0], dp1[i-1][1]+i-1);
            }
        }
        
        for(int i=s.size(), j=0;i>=1;i--,j++){
            if(s[i-1] == '1'){
                dp2[i][1] = min(dp2[i+1][1], dp2[i+1][0]+j);
                dp2[i][0] = min(dp2[i+1][0]+j+j+1, dp2[i+1][1]+j+1);
            }
            else {
                dp2[i][1] = min(dp2[i+1][1]+j+j+1, dp2[i+1][0]+j+1);
                dp2[i][0] = min(dp2[i+1][0], dp2[i+1][1]+j);
            }
        }
        
        long long ans = 1e18;
        
        for(int i=1;i<=s.size();i++)
            ans = min(ans, min(dp1[i][0]+dp2[i+1][0], dp1[i][1]+dp2[i+1][1]));
        
        return ans;
        
    }
};
```

### Optimized Solution
參考[這篇](https://leetcode.com/problems/minimum-cost-to-make-all-characters-equal/solutions/3570185/single-pass-iterative-easy-explanation-think-greedy-one-line-clean-code-c/)，只需要pass string一次，檢查operation 1與operation 2哪個需要的cost低，將其加總就是答案了。
#### 時間複雜度: $O(n)$

#### 空間複雜度: $O(1)$

程式碼:

```c++=
class Solution {
public:
    long long minimumCost(string s) {
        long long ans = 0;
        for(int i=0;i<s.size()-1;i++){
            if(s[i] !=s[i+1])
                ans += min(i+1, int(s.size())-i-1);
        }
        return ans;
    }
};
```


## [2713. Maximum Strictly Increasing Cells in a Matrix](https://leetcode.com/problems/maximum-strictly-increasing-cells-in-a-matrix/)(<font color=#FF375F>Hard</font>)

限制 :

<ul>
<li><code>m == mat.length </code></li>
<li><code>n == mat[i].length </code></li>
<li><code>1 &lt;= m, n &lt;= 10<sup>5</sup></code></li>
<li><code>1 &lt;= m * n &lt;= 10<sup>5</sup></code></li>
<li><code>-10<sup>5</sup> &lt;= mat[i][j] &lt;= 10<sup>5</sup></code></li>
</ul>

### Solution

很明顯的dp題，最單純的想法就是將每個node可以跳的最大格數算出來，如果其他node可以跳到該node，他可以跳的最大格數就等於該node可以跳的最大格數+1。

dp的pseudo式是:

$當前node可以跳到的最大格數 = max\{當前node可以跳到的node可以跳到的最大格數\} + 1$

但如果是最單純的想法去dp的話，每一次狀態轉移要check $n+m$ 次，整體複雜度變$O(n*m*(n+m))$。

要解決這題的話，可以參考[這次周賽](https://hackmd.io/ATJaMTDWQuOUoFVB9gzANQ)裡第四題的解法，**使用priority queue降低狀態轉移所需要的時間**。

因此使用priority queue去降低找到最大值的所需的時間，從$O(n+m)降低到O(log(n)+log(m))$。

但由於實務上的一些問題，我以下實作的程式碼遇到極端測資例如 **\[ \[1\]\*100000 \]**，複雜度會退化到$O(n*m*(n+m))$，實在十分抱歉。如果要看時間複雜度正確的解，可以觀看下一個使用不同方法的 Optimized Solution。
#### 時間複雜度: $O(n*m*(n+m))$

#### 空間複雜度: $O(n*m)$

程式碼:

```c++=
class Solution {
public:
    int maxIncreasingCells(vector<vector<int>>& mat) {
        int n = mat.size(), m = mat[0].size();
        
        vector<pair<int, pair<int, int> > > arr;
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                arr.push_back({mat[i][j], {i, j}});
            }
        }
        
        sort(arr.begin(), arr.end(), greater<pair<int, pair<int, int> > >());
        vector<priority_queue<pair<int, int> > > row(n), col(m);
        int ans = 0;
        
        for(auto &[val, pos]: arr){
            int sum = 0, i = pos.first, j = pos.second;
            
            vector<pair<int, int> > temp;
            while(!row[i].empty()&&row[i].top().second == val){
                temp.push_back(row[i].top());
                row[i].pop();
            }
            if(!row[i].empty())
                sum = max(sum, row[i].top().first);

            for(int k=0;k<temp.size();k++)
                row[i].push(temp[k]);
            temp.clear();
            
            while(!col[j].empty()&&col[j].top().second == val){
                temp.push_back(col[j].top());
                col[j].pop();
            }
            if(!col[j].empty())
                sum = max(sum, col[j].top().first);
            
            for(int k=0;k<temp.size();k++)
                col[j].push(temp[k]);
            temp.clear();

            sum++;
            
            ans = max(ans, sum);
            row[i].push({sum, val});
            col[j].push({sum, val});
        }
        
        return ans;
    }
};
```

### Optimized Solution

參考[這篇](https://leetcode.com/problems/maximum-strictly-increasing-cells-in-a-matrix/solutions/3570296/java-c-python-dp-solution/)的解。

想法其實非常相似，都是從dp出發。

$不過這邊的dp[i][j]的意義與我上個解不太一樣，\\這邊dp[i][j]的意思是從任意點出發到mat[i][j]這點可以跳到的最大格數，\\上一個解的dp[i][j]是從mat[i][j]開始跳，可以跳到的最大格數。$

這個解法對於每個row或column只需要使用一個變數來存取該row或column的最大值，有了最大值後只要使用dp式:

$dp[i][j] = max(row[i], col[j]) + 1$

就可以計算出該點的解答，之後利用這個解答去重新更新row[i]與col[j]的最大值，這樣重複的算，最後只要找出每個row與每個col裡的最大值就是題目所求的答案。

這個解巧妙的地方在於使用了**map來避免mat[i][j]相同值互相干擾的問題**，一次就處理完相同值的所有結果，如果套用到上一個解法的話，就不需要重新將同一個值的結果重新push回去與該元素同row與column的priority queue裡。

#### 時間複雜度: $O(n*m*log(n*m))$

#### 空間複雜度: $O(n*m)$

程式碼:

```c++=
class Solution {
public:
    int maxIncreasingCells(vector<vector<int>>& mat) {
        int n = mat.size(), m = mat[0].size();
        
        map<int, vector<pair<int, int> > > mp;
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                mp[mat[i][j]].push_back({i, j});
            }
        }
        
        vector<int> row(n), col(m);
        vector<vector<int> > dp(n, vector<int>(m));
        int ans = 0;
        
        for(auto &[val, pos]: mp){
            for(auto &[i, j]: pos)
                dp[i][j] = max(row[i], col[j]) + 1;
            
            for(auto &[i, j]: pos){
                row[i] = max(row[i], dp[i][j]);
                col[j] = max(col[j], dp[i][j]);
            }
        }
        
        for(auto &val: row)
            ans = max(ans, val);
        for(auto &val: col)
            ans = max(ans, val);
        return ans;
    }
};
```
