1. 程式人生 > >Codeforces Round #556 (Div. 2) D. Three Religions 題解 動態規劃

Codeforces Round #556 (Div. 2) D. Three Religions 題解 動態規劃

當我 uri pre 分析 code oid vector bre ons

題目鏈接:http://codeforces.com/contest/1150/problem/D
題目大意:
你有一個參考串 s 和三個裝載字符串的容器 vec[0..2] ,然後還有 q 次操作,每次操作你可以選擇3個容器中的任意一個容器,往這個容器的末尾添加一個字符,或者從這個容器的末尾取出一個字符。
每一次操作之後,你都需要判斷:三個容器的字符串能夠表示成 s 的三個不重疊的子序列。
比如,如果你的參考串 s 是:
abdabc
而三個容器對應的字符串是:

  • vec[0]ad
  • vec[1]bc
  • vec[2]ab

那麽是三個容器是可以憑借成 s 的三個不重疊的子序列的,如圖:
技術分享圖片

題目分析:
首先如果不是q次詢問的話,那麽這道題目乍看起來應該是可以使用dp或者網絡流來進行求解的。

那麽這道題目用dp比較好解。
首先我們需要開一個 nxt[N][26] 的數組,nxt[i][j] 表示以字符串 s[i] 開始第一個出現字符 ‘a‘+j 的位置。N 表示字符串 s 的長度。
那麽我們可以用 O(N*26) 的時間初始化這個數組。

然後我們開一個三維數組 dp[250][250][250] ,其中 dp[n0][n1][n2] 表示 字符串 s 匹配到 vec[0][n0]vec[1][n1]vec[2][n2] 的最小坐標。
那麽我們就能夠無推斷出狀態轉移方程:

if (!i && !j && !k) dp[0][0][0] = -1;
else {
    if (i && dp[i-1][j][k]+1 < N && nxt[dp[i-1][j][k]+1][vec[0][i-1]-'a'] != INF) {
        dp[i][j][k] = min(dp[i][j][k], nxt[dp[i-1][j][k]+1][vec[0][i-1]-'a']);
    }
    if (j && dp[i][j-1][k]+1 < N && nxt[dp[i][j-1][k]+1][vec[1][j-1]-'a'] != INF) {
        dp[i][j][k] = min(dp[i][j][k], nxt[dp[i][j-1][k]+1][vec[1][j-1]-'a']);
    }
    if (k && dp[i][j][k-1]+1 < N && nxt[dp[i][j][k-1]+1][vec[2][k-1]-'a'] != INF) {
        dp[i][j][k] = min(dp[i][j][k], nxt[dp[i][j][k-1]+1][vec[2][k-1]-'a']);
    }
}

我們可以用 O(250^3) 的時間復雜度求得一個答案,然後對於q次詢問,時間復雜度是 O(250^3*q)
但是我們註意到每次更新都知識更新三個容器中任意一個的一個值。
對於減字符串操作,我們不需要進行任何處理;
而對於增加字符串操作,我們假設三個容器的字符串個數分別為 N0N1N2,那麽:

  • 當我們往 vec[0] 中添加了一個元素之後,我們除了 N0++ 操作以外,只需要更新 dp[N0][0][0]到dp[N0][N1][N2]
  • 當我們往 vec[1] 中添加了一個元素之後,我們除了 N1++ 操作以外,只需要更新 dp[0][N1][0]到dp[N0][N1][N2]
  • 當我們往 vec[2] 中添加了一個元素之後,我們除了 N2++
    操作以外,只需要更新 dp[0][0][N2]到dp[N0][N1][N2]

所以其實對於每一次詢問,我們都只需要進行 O(250^2) 就可以了。
那麽最終我們將這道題目的時間復雜度降到了 O(q*250^2)

代碼:

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstdio>
using namespace std;
#define INF (1<<29)

int dp[255][255][255], n[3], nxt[100005][26], N, q;
vector<char> vec[3];
string s;

void init() {
    for (int i = 0; i < 26; i ++) {
        int idx = INF;
        for (int j = N-1; j >= 0; j --) {
            char c = (char)('a' + i);
            if (s[j] == c)
                idx = j;
            nxt[j][i] = idx;
        }
    }
}

void cal(int n0, int n1, int n2) {
    for (int i = n0; i <= n[0]; i ++) {
        for (int j = n1; j <= n[1]; j ++) {
            for (int k = n2; k <= n[2]; k ++) {
                dp[i][j][k] = INF;
                if (!i && !j && !k) dp[0][0][0] = -1;
                else {
                    if (i && dp[i-1][j][k]+1 < N && nxt[dp[i-1][j][k]+1][vec[0][i-1]-'a'] != INF) {
                        dp[i][j][k] = min(dp[i][j][k], nxt[dp[i-1][j][k]+1][vec[0][i-1]-'a']);
                    }
                    if (j && dp[i][j-1][k]+1 < N && nxt[dp[i][j-1][k]+1][vec[1][j-1]-'a'] != INF) {
                        dp[i][j][k] = min(dp[i][j][k], nxt[dp[i][j-1][k]+1][vec[1][j-1]-'a']);
                    }
                    if (k && dp[i][j][k-1]+1 < N && nxt[dp[i][j][k-1]+1][vec[2][k-1]-'a'] != INF) {
                        dp[i][j][k] = min(dp[i][j][k], nxt[dp[i][j][k-1]+1][vec[2][k-1]-'a']);
                    }
                }
            }
        }
    }
}

int main() {
    cin >> N >> q >> s;
    init();
    cal(0, 0, 0);
    while (q --) {
        string tmps1, tmps2;
        int tmpnum;
        cin >> tmps1 >> tmpnum;
        if (tmps1 == "+") {
            cin >> tmps2;
            vec[tmpnum-1].push_back(tmps2[0]);
            n[tmpnum-1] ++;
            switch(tmpnum) {
                case 1:
                    cal(n[0], 0, 0);
                    break;
                case 2:
                    cal(0, n[1], 0);
                    break;
                case 3:
                    cal(0, 0, n[2]);
                    break;
            }
        } else {
            n[tmpnum-1] --;
            vec[tmpnum-1].pop_back();
        }
        cout << ( dp[n[0]][n[1]][n[2]] == INF ? "NO" : "YES" ) << endl;
    }
    return 0;
}

Codeforces Round #556 (Div. 2) D. Three Religions 題解 動態規劃