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;
}