【牛客練習賽60:C】操作集錦(dp+子序列計數)

傳送門

  • 題目
    給出長度爲的字符串,求有多少種不同的長度爲的子序列。(1n1e3,0kn)(1≤n≤1e3, 0≤k≤n)
  • 思路
    • 空串也是一種合法的子序列,所以特判k=0k=0

    • 二維dp求解(當然也可以一維,我原本的做法是一維的,比下面的解法稍微麻煩一點,就不講啦)

      • 解法一:O(26n2)O(26n^2)
        dp[i][j]dp[i][j]表示前ii個字符中長度爲jj且以s[i]s[i]爲結尾的子序列種類數(1in,1ji)(1≤i≤n, 1≤j≤i)
        nxt[i][j]nxt[i][j]表示ii後面j+aj+'a'第一次出現的位置(0i<n,0j<26)(0≤i<n, 0≤j<26),這樣做是爲了避免重複統計答案。
        轉移方程:

      dp[x][j+1]+=dp[i][j]dp[x][j+1]+=dp[i][j] (x=nxt[i][p]&&x!=0,0p<26)(x=nxt[i][p]\&\&x!=0,0≤p<26)
      ans=i=kndp[i][k]ans=\sum_{i=k}^{n}dp[i][k]
      注意初始化:dp[0][0]=1dp[0][0]=1

      • 解法二:O(n2)O(n^2)
        dp[i][j]dp[i][j]表示前ii個字符中長度爲jj的子序列種類數(1in,1ji)(1≤i≤n, 1≤j≤i)
        last[i]last[i]表示i+ai+'a'上一次出現的位置(0i<26)(0≤i<26)
        轉移方程:
        dp[i][j]={dp[i1][j]+dp[i1][j1]last[s[i]a]==0dp[i1][j]+dp[i1][j1]dp[x1][j1]x=last[s[i]a]!=0dp[i][j]=\begin{cases} dp[i-1][j]+dp[i-1][j-1] &last[s[i]-'a']==0\\ dp[i-1][j]+dp[i-1][j-1]-dp[x-1][j-1] &x=last[s[i]-'a']!=0 \end{cases}
        注意爲了防止dp[i][j]的結果爲負數,需要+mod再取模

      ans=dp[n][k]ans=dp[n][k]

      注意初始化dp[i][0]=1dp[i][0]=1 (0in)(0≤i≤n)

  • ac代碼:
    • 解法一:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e3+10;
ll dp[maxn][maxn];
int nxt[maxn][30];
int n, k;
char s[maxn];
int main()
{
    scanf("%d %d", &n, &k);
    scanf("%s", s+1);
    if(k==0) {printf("1\n"); return 0;}
    for(int i = 0; i < n; i++)
        for(int j = i+1; j <= n; j++)
            if(!nxt[i][s[j]-'a']) nxt[i][s[j]-'a'] = j;
    dp[0][0] = 1;
    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j <= min(i, k); j++)
        {
            for(int p = 0; p < 26; p++)
            {
                int x = nxt[i][p];
                if(x!=0) dp[x][j+1] = (dp[x][j+1]+dp[i][j])%mod;
            }
        }
    }
    ll ans = 0;
    for(int i = k; i <= n; i++) ans = (ans+dp[i][k])%mod;
    printf("%lld\n", ans);
    return 0;
}
  • 解法二:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod = 1e9+7;
const int maxn = 1e3+10;
ll dp[maxn][maxn];
int last[maxn];
int n, k;
char s[maxn];
int main()
{
    scanf("%d %d", &n, &k);
    scanf("%s", s+1);
    if(k==0) {printf("1\n"); return 0;}
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++)
    {
        dp[i][0] = 1;
        for(int j = 1; j <= min(i, k); j++)
        {
            dp[i][j] = (dp[i-1][j]+dp[i-1][j-1])%mod;
            if(last[s[i]-'a']!=0) dp[i][j] = (dp[i][j]-dp[last[s[i]-'a']-1][j-1]+mod)%mod;
        }
        last[s[i]-'a'] = i;
    }
    printf("%lld\n", dp[n][k]%mod);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章