abc215_e Chain Contestant(状压dp)

题目链接

题目大意

  给你一个字符串,问你有多少子序列s,满足相同的字符都是连续出现的,当两个子序列中有一个字符的位置不同时两个子序列不同。

解题思路

  看到只有10种字符,并且n只有1000大概就能想到做法了。考虑dp,对于当前字符i,如果之前有一个以i结尾的合法子序列,那么在它后面加上i也是合法的;如果不是以i结尾的子序列,那么之前就不能出现i,否则相同字符不能连续出现。所以我们需要记录两样东西,一样是前面出现的字符集合,一样是字符集合中结尾的字符,前者可以用10位二进制表示,后者用10个int表示即可。
  有一个小细节需要注意一下,我们不能只考虑从前面的状态转移到当前的状态,因为还有一种方案,是前面没有字符,只有当前一个字符,记得补上去。

代码

const int maxn = 1e3+10;
const int maxm = 2e5+10;
int dp[2][1<<10][10];
int main() {
    IOS;
    int n; cin >> n;
    string s; cin >> s;
    for (int i = 0; i<n; ++i) {
        int t = s[i]-'A';
        int now = i&1, pre = now^1;
        memcpy(dp[now], dp[pre], sizeof(dp[pre]));
        ++dp[now][1<<t][t];
        for (int j = 0; j<(1<<10); ++j)
            if (j&(1<<t)) {
                for (int k = 0; k<10; ++k) {
                    if (k==t) dp[now][j][t] += dp[pre][j][k];
                    else dp[now][j][t] += dp[pre][j^(1<<t)][k];
                    dp[now][j][t] %= MOD;
                }
            }
    }
    int ans = 0;
    for (int i = 0; i<(1<<10); ++i)
        for (int j = 0; j<10; ++j)
            ans = (ans+dp[(n-1)&1][i][j])%MOD;
    cout << ans << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/shuitiangong/p/15651924.html