CodeForces 1426F Number of Subsequences


題意

給定一個長度為 \(n\) 的串,只包含 abc 和通配符。通配符可以替換 abc 的一個。求所有得到的字符串中子序列 abc 出現的次數,對 \(10^9+7\) 取模。

\(\texttt{Data Range:}n\leq 2\times 10^5\)

題解

哇哈哈哈我智商終於恢復了。

比較套路,但其實這個東西我一開始是用類似於期望的東西來想的。

記通配符的數量為 \(m\)

考慮設 \(f_{i,j}\) 表示所有 \(3^m\) 個字符串的前 \(i\) 個字符中,子序列 aababc 的數量之和。

首先當給定字符串的第 \(i\) 個字符為 a 的時候,有如下轉移:

\[f_{i,1}=f_{i-1,1}+3^m,f_{i,2}=f_{i-1,2},f_{i,3}=f_{i-1,3} \]

b 的時候有如下轉移:

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

c 的時候有如下轉移:

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

這三個轉移都很平凡,這里不多贅述。

接下來是為通配符的情況,需要討論一下。

注意到我們肯定可以將所有 \(3^{m}\) 個字符串中直到 \(i-1\) 的前綴划分為三組,每組的字符串相同。

所以說每組字符串的中的 aababc 的數量變成了 \(\frac{f_{i-1,1}}{3}\)\(\frac{f_{i-1,2}}{3}\)\(\frac{f_{i-1,3}}{3}\)

於是考慮將第一組的后面加一個 a,第二組加一個 b,第三組加一個 c。這樣子我們就可以寫出一個轉移方程:

\[\begin{cases}f_{i,1}=\frac{f_{i-1,1}+3^m}{3}+\frac{2f_{i-1,1}}{3}\\f_{i,2}=\frac{f_{i-1,2}+f_{i-1,1}}{3}+\frac{2f_{i-1,2}}{3}\\f_{i,3}=\frac{f_{i-1,3}+f_{i-1,2}}{3}+\frac{2f_{i-1,3}}{3}\end{cases} \]

整理一下得到以下轉移:

\[f_{i,1}=f_{i-1,1}+3^{m-1},f_{i,2}=f_{i-1,2}+\frac{f_{i-1,1}}{3},f_{i,3}=f_{i-1,3}+\frac{f_{i-1,2}}{3} \]

線性 DP 就沒了。

有一個加強版就是說多組詢問求任意子段的答案。注意到 \(f_{i}\) 只與 \(f_{i-1}\) 有關所以可以寫成一個 \(4\times 4\) 的矩陣,然后用線段樹維護矩陣乘積就好了。可能會算重,於是除掉一下就差不多了。

代碼

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51,MOD=1e9+7,INV3=333333336;
ll n,m,pw=1;
char ch[MAXN];
ll f[MAXN][3];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
int main()
{
    n=read(),scanf("%s",ch+1);
    for(register int i=1;i<=n;i++)
    {
        ch[i]=='?'?m++,pw=(li)pw*3%MOD:1;
    }
    for(register int i=1;i<=n;i++)
    {
        f[i][1]=f[i-1][1],f[i][2]=f[i-1][2],f[i][3]=f[i-1][3];
        if(ch[i]=='a')
        {
            f[i][1]=(f[i][1]+pw)%MOD;
        }
        if(ch[i]=='b')
        {
            f[i][2]=(f[i][2]+f[i-1][1])%MOD;
        }
        if(ch[i]=='c')
        {
            f[i][3]=(f[i][3]+f[i-1][2])%MOD;
        }
        if(ch[i]=='?')
        {
            f[i][1]=(f[i][1]+(li)pw*INV3%MOD)%MOD;
            f[i][2]=(f[i][2]+(li)f[i-1][1]*INV3)%MOD;
            f[i][3]=(f[i][3]+(li)f[i-1][2]*INV3)%MOD;
        }
    }
    printf("%d\n",f[n][3]);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM