1. 程式人生 > 實用技巧 >【秋葉收藏集】

【秋葉收藏集】

傳送門

題意

給出一個字串,只包括兩個字元 'r' , 'y',現在可以把 'y' 變成 'r' ,把 'r' 變成 'y',問最少需要多少次,才能把這個字串變成 'r...y...r...'模式。

思路

一般這種題目我都是通過列舉端點解決。

字串下標從 1 開始,設兩個分段點分別為 \(p_1\)\(p_2\)

那麼 區間\([1,p_1]\) 應該全部變為 \('r'\),區間\([p_1+1,p_2]\) 應該變成 \('y'\),區間\([p_2+1,len]\)應該變成 \('r'\)

我們先列舉 \(p_1\) 的位置,再列舉 \(p_2\) 的位置,得到 \(p_2\) 為任意值時使得區間 \([p_1+1,len]\)

合法的操作次數。

比如 \(yyyrryy\)

\(p_1\) 為 1 時,\(p_2\) 分別為 2,3,4,5,6時,使得區間 \([p_1+1,len]\)合法的操作次數分別為:

3 2 3 4 3

這時我們來看當 \(p_1\) 的位置往後遞推一個時相應的合法操作次數:

2 3 4 3 向後推了一個y

  3 4 3 向後推了一個y

    3 2 向後推了一個r

      1 向後退了一個r

我們可以發現當往後推的這一個字元為 \('y'\) 時,操作次數不會發生變化,但是如果向後推的是 \('r'\),那麼整體會 -1。即:對於每一個 \(p_2\)\(p_1+1\)

也就意味著 \([p_1+1,p_2]\) 這一段少一個字元,如果少了一個 \('r'\) :區間\([p_1+1,p_2]\)操作次數就會減少,否則不變。而 \([p_2+1,len]\) 的操作次數沒有發生變化。

首先當 \(p_1 ==1\) 時,可以求出此時所有 \(p_2\) 的操作次數,因為這些操作次數只會同時改變。

所以我們可以在 \(p_1==1\) 的時候就求出所有 \(p_1\) 的最佳位置。

對於任意一個 \(p_1\) 其答案為:

[1,p1]的'y'個數 + p1=1時求出的該位置p2的最佳位置的操作次數 - [2,p1]'r'的數量

程式碼

class Solution {
public:
    int minimumOperations(string leaves)
    {
        int N = 1e5 + 10;
        int inf = 0x3f3f3f3f;
        int prer[N], prey[N], minn[N],ans[N];
        int len = leaves.size();
        leaves = "1" + leaves;
        for (int i = 1; i <= len; i++) {
            prer[i] = prer[i - 1] + (leaves[i] == 'r');
            prey[i] = prey[i - 1] + (leaves[i] == 'y');
        }
        minn[len] = inf;
        for (int i = len - 1; i>1; i--) {
            ans[i] = prer[i] - prer[1] + prey[len] - prey[i];//求出當p1為1時,p2為i時,使得區間[p1+1,len]合法時的次數
            minn[i] = min(ans[i], minn[i + 1]);//求出當p1為i時,使得區間[p1+1,len]合法的最少的操作次數(需減去[2,i]出現過的'r'的數量)
        }
        int rel = inf;
        for (int i = 1; i < len - 1; i++) {//列舉p1
            int now = prey[i] + minn[i + 1] - (prer[i] - prer[1]);
            rel = min(rel, now);
        }
        return rel;
    }
};