t8m8-mem8

競プロとか、学んだことを書いていきます。

SRM 400 Div1 Medium: ReversalChain

問題

0と1から成る文字列initをgoalに変換する。できる操作は区間(i, j)の反転(ビットの反転ではなく、文字列の反転)のみで、これをr(i, j)と書く。  r(i_1, j_1),r(i_2,j_2),..,r(i_m,j_m) が与えられた時に { \displaystyle
i_1{\leq}i_2{\leq} ... {\leq}i_m\  {\land} \ \  j_1{\geq}j_2{\geq}...{\geq}j_m
} が成り立つとき、これをreversal chainと呼ぶ。initをgoalに変換する際の最小のreversal chainの長さ(操作の回数)を求めよ。

制約

 1{\leq}|init|{\leq}50

解法

dp[len][i][j][k] := initのi番目の文字から始まる長さlenの部分文字列と、goalのj番目の文字から始まる長さlenの部分文字列を一致させるのに必要な最小の操作回数。k=0で偶数回反転、k=1で奇数回反転。

final int INF = Integer.MAX_VALUE/2;

public int minReversal(String init, String goal) {
    int n = init.length();

    int[][][][] dp = new int[n+1][n][n][2];

    for (int i=0; i<n; i++) {
        for (int j=0; j<n; j++) {
            for (int k=0; k<2; k++) {
                if (init.charAt(i) != goal.charAt(j))
                    dp[1][i][j][k] = INF;
            }
        }
    }

    for (int len=2; len<=n; len++) {
        for (int i=0; i<=n-len; i++) {
            for (int j=0; j<=n-len; j++) {
                for (int k=0; k<2; k++) {
                    int a = init.charAt(i) != goal.charAt(j) ? INF : dp[len-1][i+1][j+1][0] + k;
                    int b = init.charAt(i+len-1) != goal.charAt(j+len-1) ? INF : dp[len-1][i][j][0] + k;
                    int c = init.charAt(i) != goal.charAt(j+len-1) ? INF : dp[len-1][i+1][j][1] + (k+1)%2;
                    int d = init.charAt(i+len-1) != goal.charAt(j) ? INF : dp[len-1][i][j+1][1] + (k+1)%2;

                    dp[len][i][j][k] = Math.min(Math.min(a,b),Math.min(c,d));
                }
            }
        }
    }

    return dp[n][0][0][0] == INF ? -1 : dp[n][0][0][0];
}