mayoko’s diary

プロコンとかいろいろ。

SRM 519 div1 med:RequiredSubstrings

言われてみると桁DPの一般化っぽい感じ。

解法

主となるアイデアは桁DPとほとんど変わりません。つまり,

ある文字列の状態(stateとする)にaからzのいずれかの文字を追加した時の次の状態(nextstateとする)とした時に,

dp[i+1][nextstate] += dp[i][state]

とするようなイメージです。

この解法で解くときに注意しなければならないのは,

・状態をどのように定義するか
・状態の遷移はどうなるか
・結局答えを求めるにはどうすればええんや

コードでそれぞれstate, nextState, stateAddでやっています。words[]のstringそれぞれの部分文字列を状態として,末尾に文字を追加した時の次の状態をnextStateにして,ある状態での文字列の末尾にwords[i]の文字があればi文字目があるというフラグを立てておきます。

これを使ってdpすればOKです。

const ll MOD = 1e9+9;

ll dp[55][500][64];

class RequiredSubstrings {
public:
    int solve(vector <string> words, int C, int L) {
        map<string, int> state;
        int S = 0;
        int n = words.size();
        // stateの列挙
        for (int i = 0; i < n; i++) {
            for (int j = 0; j <= words[i].size(); j++) {
                string tmp = words[i].substr(0, j);
                if (state.find(tmp) == state.end()) {
                    state.insert(make_pair(tmp, S++));
                }
            }
        }
        cout << S << endl;
        // stateの遷移
        vector<vi> nextState(S, vi(26));
        for (auto p : state) {
            for (int i = 0; i < 26; i++) {
                string nstr = p.first + (char)('a'+i);
                while (state.find(nstr) == state.end()) nstr = nstr.substr(1);
                nextState[p.second][i] = state[nstr];
            }
        }
        // stateの判定
        vector<int> stateAdd(S);
        for (auto p : state) {
            string str = p.first;
            int s = p.second;
            int add = 0;
            for (int j = 0; j < n; j++) {
                if (str.size() < words[j].size()) continue;
                if (str.substr(str.size()-words[j].size()) == words[j]) add |= (1<<j);
            }
            stateAdd[s] = add;
        }
        // dp
        memset(dp, 0, sizeof(dp));
        dp[0][0][0] = 1; // l, state, flag
        for (int i = 0; i < L; i++) for (int s = 0; s < S; s++) for (int flag = 0; flag < (1<<n); flag++) {
            if (dp[i][s][flag] == 0) continue;
            for (int j = 0; j < 26; j++) {
                int ns = nextState[s][j];
                int nflag = flag;
                nflag |= stateAdd[ns];
                (dp[i+1][ns][nflag] += dp[i][s][flag]) %= MOD;
            }
        }
        ll ans = 0;
        for (int i = 0; i < (1<<n); i++) {
            if (__builtin_popcount(i) != C) continue;
            for (int j = 0; j < S; j++) {
                (ans += dp[L][j][i]) %= MOD;
            }
        }
        return (int)ans;
    }