AOJ 2449 Connect

解法

これは典型問題です.
$i$ 行目の $j$ 番目に文字を置くかどうか考えた時に関係してくるのは,$i$ 行目の $1$ から $j - 1$ 列目と,$i - 1$ 行目の $j$ から $C$ 列目だけです.
なので結局,一行分の状態をビットで持つ bitDP にできます.

$dp[i][j][S] := i$ 行目の $j$ 列目まで見た時,文字が配置されている状況が $S$ であるときの最大値
$S$ の下位 $j$ ビットが今の行の状態,上位 $C - j$ ビットが直前の行の状態を表しています.

$i$ 行目 $j$ 列目の位置に文字を配置する場合,直前に同じ文字が置かれているかと,前の行の同じ列に同じ文字が置かれているかを見ます.
今配置しようとしている文字のインデックスは $bitcount(S \& (1 << j) - 1)$ で得られます.
上の行で配置されている文字のインデックスは,後ろから何番目であるかを考えれば,$|s_i| - bitcount(S >> j)$ で得られます.

使わない選択をする場合は,あとに配置できる余地が十分であるかを確認する必要があります.
その他,$S$ の状態でありえないものは適宜無視すればよいです.

ソースコード

#include <bits/stdc++.h>
using namespace std;

int main() {
    int R, C;
    cin >> R >> C;
    vector<string> s(R);
    for(int i = 0; i < R; ++i) {
        cin >> s[i];
    }
    vector<int> bitcount(1 << C);
    for(int i = 0; i < 1 << C; ++i) {
        bitcount[i] = __builtin_popcount(i);
    }

    vector<int> dp(1 << C, -1);
    dp[0] = 0;
    for(int i = 0; i < R; ++i) {
        for(int j = 0; j < C; ++j) {
            vector<int> nxt(1 << C, -1);
            for(int S = 0; S < (1 << C); ++S) {
                if(dp[S] == -1) {
                    continue;
                }
                int idx = bitcount[S & (1 << j) - 1];
                if(idx < s[i].size()) {
                    int t = dp[S];
                    if(idx > 0 && (S & 1 << (j - 1)) && s[i][idx - 1] == s[i][idx]) {
                        t++;
                    }
                    if(i > 0 && (S & 1 << j) && s[i][idx] == s[i - 1][s[i - 1].size() - bitcount[S >> j]]) {
                        t++;
                    }
                    nxt[S | 1 << j] = max(nxt[S | 1 << j], t);
                }
                if(C - j > s[i].size() - idx) {
                    nxt[S & ~(1 << j)] = max(nxt[S & ~(1 << j)], dp[S]);
                }
            }
            dp.swap(nxt);
        }
    }
    cout << 2 * *max_element(begin(dp), end(dp)) << endl;
}