題解/算法 {8028. 执行操作使两个字符串相等}

題解/算法 {8028. 执行操作使两个字符串相等}

@LINK: https://leetcode.cn/problems/apply-operations-to-make-two-strings-equal/;

vector<int> A為: 如果S1[i] != S2[i] 則執行A.push_back( i) (即A裡是所有(需要操作的下標), 而且是遞增的);
因此 此時我們只需要關注這個A序列, 清空這個A序列 就是我們的目的;
對於操作1:(選擇A裡的兩個元素 代價是X 將他倆扔出去), 操作2:(選擇A裡的兩個元素a,b 代價是|a-b| 把他倆扔出去) (這個操作2是我們轉換後的形式 因為對於[0,1,2,3] 他可以變成(01) (12) (23) 即操作3次3-0次);

此時問題轉換為: 通過這兩個操作 將序列A清空的最小代價;

顯然如果A.size()為奇數 這是無解的;

性質1: 操作2所選擇的元素a,b 一定是A裡相鄰的;
. 反證, 比如x...a,c,b...x 你選擇的是操作2(ab) 那麼這個c 要麼是操作1cx (此時變成操作2(ac) 操作1(bx) 代價會變小), 要麼是操作2cx (此時變成操作2(ac) 操作2(bx) 代價會變小);

性質2: 操作1所選擇的元素 (a1,b1) (a2,b2) ..., 一定可以滿足b1 < a2, b2 < a3, ...;
. 因為 操作1的代價是固定的X, 你交換他們的次序 比如(1,5) (2,10)(1,2) (5,10) 是完全一樣的代價;

此時最終的A序列是形如 [ a, b, c, (d,e), f, (g,h), (i,j), k, l] ((d,e)表示操作2);

{ 錯誤

貪心; 從A開頭開始 如果A[i+1] - A[i] < X 那麼就使用操作2;
這是錯誤的… 比如[0, 2, 3, 6] 你會選擇[(0,2), 3, 6] 但答案是[0, (2,3), 6];
你可能認為 那再反向的處理一遍, 也是錯誤的… 當你選擇貪心時 沒有十分把握時 就是在冒險…

}

其實這很容易轉換為DP問題;
. 你可能定義為(i,j): 前i個元素裡 有j個元素是沒被選擇 即有(i-j)個元素是通過操作2進行了配對 的最小代價 這樣DP定義 就複雜很多 因為你只是對操作2進行了DP 沒考慮操作1, 其實你可以同時考慮這兩個操作;
最好的定義DP是: (i): A[0...i]這些元素(i為奇數) 通過操作1|2 將這些元素進行配對的最小代價, 你可能疑問 對於[a, (b,c), d]這種情況 你的DP 在DP[c]時 並沒有記錄a, (b,c)這個狀態 如何轉換呢? 這確實是沒法進行轉義的, 但是結合上面講的性質1|2, 這個DP比較特殊;
對於A[0...i] (共偶數個元素) 此時你要對A[i]進行配對, 要麼A[i-1],A[i]配對 前面為DP[i-2], 要麼是A[...,j-1] A[j], A[j+1,...,i-1], A[i] 此時根據性質1|2 中間的A[j+1,...,i-1] 他一定是形如(A[j+1] A[j+2]), (A[j+2] A[j+3]), ..., (A[i-2] A[i-1]) 因此 這個值可以暴力的計算, 在你往前枚舉j時 同時維護這個值;

@DELI;

#代碼#

int minOperations(string S1, string S2, int X) {
    vector< int> A;
    int N = S1.size();
    FOR_( i, 0, N-1){
        if( S1[i] != S2[i]){ A.push_back( i);}
    }
    if( A.size() & 1){ return -1;}
    N = A.size();
    if( N == 0){ return 0;}
    vector< int> DP( N);
    for( int i = 1; i < N; i += 2){ // 只遍歷偶數
        DP[ i] = min( X, A[i] - A[i-1]);
        if( i-1 > 0){ DP[ i] += DP[ i-2];}
        
        int sum = 0;
        for( int j = i - 3; j >= 0; j -= 2){ // 注意 不是`j-=3`;
            sum += (A[j+2] - A[j+1]);
            int ANS = sum + X;
            if( j > 0){ ANS += DP[j - 1];}
            MinSelf_( DP[i], ANS);
        }
    }
    return DP[ N-1];
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值