zoukankan      html  css  js  c++  java
  • CodeForces

    题目链接

    题目大意

      给一个字符串问这个字符串k个不同子串和的最小值。

    解题思路

      很明显算出来空串的个数,长度为1的不同子串的个数,长度为2的不同子串的个数...长度为n的不同子串的个数,然后就可以计算出答案了。
      长度为m的不同子串,即从n个字符里头选m个字符的不同选法,即C(n, m),等等,如果字符串是ababab,那么长度为2的不同子串数是C(6, 2)吗?显然不是。考虑"abab"计算长度为2的子串,在第一个b的时候计算了ba,在第二个b的时候计算了三个ba,但实际上应该算成一个。我们发现当像个字符相同的时候,左边那个字符之前的方案和这两个字符中的任意一个组合都是重复的方案,所以我们每次在计算时减去前一个字符构成的方案即可。
      我们设dp[i][j]表示前i个字符里选j个的方案,可以用组合数的状态转移方程,dp[i][j] = dp[i-1][j-1]+dp[i-1][j](选第i个,剩下的i-1里选j-1个 or 不选第i个,剩下的i-1个里面选j个)。然后减去重复的,也就是前一个字符str[i]所在的方案数dp[pre[str[i]]-1]j-1

    代码

    const int maxn = 1e2+10;
    ll dp[maxn][maxn];
    char str[maxn]; int pre[200];
    int main() {
        IOS;
        int n; ll k; cin >> n >> k;
        cin >> str+1;
        for (int i = 0; i<=n; ++i) dp[i][0] = 1;
        for (int i = 1; i<=n; ++i) { 
            for (int j = 1; j<=i; ++j) {
                dp[i][j] += dp[i-1][j-1]+dp[i-1][j];
                if (pre[str[i]]) dp[i][j] -= dp[pre[str[i]]-1][j-1];
                if (dp[i][j]>k) dp[i][j] = k;
            }
            pre[str[i]] = i;
        }
        ll sum = 0;
        for (int i = n; i>=0; --i) {
            if (k>dp[n][i]) {
                k -= dp[n][i];
                sum += dp[n][i]*(n-i);
            }
            else {
                sum += 1LL*k*(n-i);
                k = 0;
            }
        }
        if (k>0) sum = -1;
        cout << sum << endl;
        return 0;
    }
    
  • 相关阅读:
    USACO 2017 US Open Contest Gold T1: Bovine Genomics
    自己瞎调的一些曲子
    NOIP前要干的一些事
    【[BJOI2018]链上二次求和】
    【MdOI2020】Path
    【[SDOI2018]旧试题】
    [IOI2018]werewolf狼人
    洛谷p5444 [APIO2019]奇怪装置
    洛谷p4211 [LNOI2014] LCA
    洛谷P2805 [NOI2009] 植物大战僵尸
  • 原文地址:https://www.cnblogs.com/shuitiangong/p/15251956.html
Copyright © 2011-2022 走看看