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;   
}
posted @ 2021-08-18 20:43  shuitiangong  阅读(83)  评论(0编辑  收藏  举报