Codeforcecs1183H Subsequence(hard version) 求字符串本质不同子序列个数

题目链接:https://www.luogu.org/problem/CF1183H

题意:给你一个长为n(100)的字符串,你需要找出k(1e12)个它本质不同的子序列,且没找出一个子序列的花费为n-len(子序列),求最小花费

分析:很明显想让花费最小,就从长度最大的子序列开始找,就转化成找每个长度的本质不同的子序列有多少个

我们用dp[i][j]表示从第i个字符开始,长度为j的子序列个数有多少个。

直接转移的话会出现重复,比如baa,你就会找出两个ba

所以对于每一个i,我们用一个vis数组标记,只转移相同字符第一个出现的那一个

为了保证序列是一个个加的,我们dp的第一维是长度Len

之后贪心寻找答案即可

注意:因为空串也是一个子序列,初始化的时候i=0的也要考虑,并且之后每个非空序列其实都相当于包含了一个空串,循环dp求解的时候也从i=0开始,最后寻找答案的时候就直接从dp[0][len]到dp[0][1]了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
const int maxn=2e5+7;
const int N=1e4;
char s[110];
ll dp[210][210];
int vis[100];
int main(){
    int n;ll k;cin>>n>>k;
    k--;//第一个选的全串没有花费 
    scanf("%s",s+1);
    for(int i=0;i<=n;i++)dp[i][1]=1;
    for(int len=2;len<=n;len++){
        for(int i=0;i<=n;i++){
            memset(vis,0,sizeof(vis));
            for(int k=i+1;k<=n;k++){
                if(!vis[s[k]-'a']){
                    vis[s[k]-'a']=1;
                    dp[i][len]+=dp[k][len-1];
                }
            }
        }
    }
    ll ans=0;
    //cout<<dp[0][4]<<" 233\n";
    for(int len=n;len>0&&k!=0;len--){
        if(dp[0][len]<=k){
            k-=dp[0][len];
            ans+=1ll*(n+1-len)*dp[0][len];
        }
        else{
            ans+=1ll*(n+1-len)*k;
            k=0;
        }
    }
    if(k>0)printf("-1\n");
    else printf("%I64d\n",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/qingjiuling/p/11622034.html