abc214_f Substrings(dp)
题目大意
给你一个字符串,你可以标记一个子序列,子序列中的字符不能相邻,剩下的删除掉,问最后能形成多少种不同的子序列。
解题思路
可以设dp[i]表示以i结尾并且最后剩下的子序列有第i位时有多少不同的子序列。设dp[1] = dp[0] = 1, 那么我们的状态转移方程就是\(dp[i] = \sum dp[j](j \leq i-2)\)。但是这样时有重复计算的,给一个字符串"abcabc",当i处于第2个'c'的位置时,我们的状态计算为dp[6] = dp[4]+dp[3]+dp[2],发现不能再加dp[1]了,因为这相当于"ac"这个子序列,但是之前计算第1个'c'的位置时就已经计算过"ac"了,再计算就重复了,也就是说如果j+1和i相等的话,j就不应该再往前枚举了。
这就结束了吗?还没完,比如"aaaaaa",计算第一个'a'的时候贡献为1,计算第二个'a'的时候贡献也为1,是不是不太对?前两个对应的子序列都是'a',明显重复了。这时候我们可以在字符串前面加一个奇怪的符号,等于说计算的字符串整体右移了一个字符,然后只让dp[0]初始化为1,从2开始计算,在j+1和i相等的时候,j就不再往前枚举了,就可以避免重复计算了。
const int maxn = 2e5+10;
const int maxm = 2e5+10;
ll dp[maxn];
int main(void) {
IOS;
string s; cin >> s;
s = "&^"+s;
dp[0] = 1;
int n = s.size();
for (int i = 2; i<n; ++i) {
for (int j = i-2; j>=0; --j) {
dp[i] = (dp[i]+dp[j])%MOD;
if (s[j+1]==s[i]) break;
}
}
ll sum = 0;
for (int i = 2; i<n; ++i) sum = (sum+dp[i])%MOD;
cout << sum << endl;
return 0;
}