Atcoder ABC214 F - Substrings

Description

给你一个字符串 \(S\),从中选出一个字符串 \(T\),要求相邻两个位置不能同时选,不能改变顺序,求能选出多少种不同的 \(T\)。对 \(10^9 + 7\) 取模。
\(n \le 2 \times 10^5\)

Solution1

考虑 DP。

\(f_{i,0/1}\) 表示考虑到第 \(i\) 位,第 \(i\) 位选不选的方案数。

如果不考虑重复的情况,显然有:

\[f_{i,0} = f_{i-1,0} + f_{i-1,1} \]

\[f_{i,1} = f_{i-1,0} \]

这个和树上点独立集一样的。

考虑如何去重。

设第 \(i\) 位的字母为 \(c\),稍微改一下转移方程:

\[f_{i,1} = f_{i-1,0} - \sum_{j=1}^{i-1} f_{j,1} [s_j = c] \]

为什么把前 \(i-1\) 位中结尾为 \(c\) 的方案去掉?

仔细考虑一下,减去的这些方案数也和当前位置 \(c\) 一样,都是在最后面放一个 \(c\) 为了避免重复就需要去掉这一部分的方案。

考虑用二十六棵树状数组去维护前面 \(i-1\) 个位置以某个字母结尾的方案数和。转移方程变为:

\[f_{i,1} = f_{i-1,0} - S[c].Query(i-1) \]

就可以在 \(\mathcal O(n \log n)\) 的时间内完成转移。

Code1

只放关键部分代码:

int n;
char s[MAXN];
int f[MAXN][2];

struct Bit {
    int sum[MAXN];
    int lb(int x) { return x & -x; }
    void Modify(int x, int k) { for(; x <= n; x += lb(x)) sum[x] = (sum[x] + k) % mod; }
    int Query(int x) { int res = 0; for(; x; x -= lb(x)) res = (res + sum[x]) % mod; return res; }
}S[26];

signed main()
{
	cin >> s + 1;
    n = strlen(s + 1);
	f[1][1] = 1, S[s[1] - 'a'].Modify(1, f[1][1]);
	for(int i = 2; i <= n; ++i) {
	    f[i][0] = (f[i - 1][0] + f[i - 1][1]) % mod;
	    f[i][1] = (f[i - 1][0] + 1 - S[s[i] - 'a'].Query(i - 1) + mod) % mod;
	    S[s[i] - 'a'].Modify(i, f[i][1]);
    }
    printf("%lld\n", (f[n][0] + f[n][1]) % mod);
    return 0;
}

Solution2

下面介绍题解的做法。

\(f_i\) 表示以 \(s_i\) 为结尾的方案数。

设从后向前看第一个和 \(s_i\) 相同的字符的位置为 \(k\)。如果没有的话 \(k=0\)

转移方程:

\[f_i = \sum_{j=k}^{i-1} f_j \]

去重的道理和上面的差不多吧:如果从 \(<k\) 的位置转移过来,那么得到的字符串方案其实已经在 \(f_k\) 处统计过了。

转移的时候直接暴力转移。

看上去复杂度很高,但它是均摊是 \(\mathcal O(n)\) 的。

为了使每个位置都尽可能向前转移,一个的最劣的字符串是:

\(abcdef...efdcba\)

所以最坏复杂度也只有 \(\mathcal O(26n)\)

Code2

扔一个 std

#include<bits/stdc++.h>
using namespace std;
int main(){
    string S; cin >> S;
    int N = S.size();
    vector<long long> dp(N+2); dp[0] = 1;
    for(int i = 0; i < N; i++){
        for(int j = i-1; ; j--){
            dp[i+2] = (dp[i+2] + dp[j+1]) % 1000000007;
            if(j == -1 || S[j] == S[i]) break;
        }
    }
    long long ans=0;
    for(int i = 2; i < N+2; i++) ans += dp[i];
    cout << ans%1000000007 << endl;
}
posted @ 2021-10-19 10:12  Suzt_ilymtics  阅读(63)  评论(0编辑  收藏  举报