• codeforces 645 E. Intellectual Inquiry


    一个字符串,由前k个字母组成,长度为m + n,其中前m个字符已经确定,后面n个由你自由选择,

    使得这个串的不同的子序列的个数最多,空串也算一个子序列。

    1 <= m <= 10^6,0 <= n <= 10^6,1 <= k <= 26

    首先,我们考虑n = 0的情况,

    问题就为给定一个字符串,求它有多少个不同的子序列。

    pre[i]表示字母i最后出现的位置,初始化为0

    f[i]表示以第i个字符结尾的与前面已经出现的子序列都不同的子序列个数

    g[i] = ∑0<=j<=if[j]

    则我们知道f[i] = ∑pre[i]<=j<=i-1f[j] 

    则 if pre[i] == 0  then f[i] = g[i-1]

        if pre[i] > 0    then f[i] = g[i-1] - g[pre[str[i]]-1]

    答案就是g[m]

    那如果n > 0 呢?

    从f的递推式我们知道,要使得f[i]最大,i处填的字符应该是pre值最小的i

    那么我们接着遍历,每次拿pre最小的字符,更新f和pre值

    最终答案就是g[n + m]

    代码:

                                               
      //File Name: cf645E.cpp
      //Created Time: 2017年01月05日 星期四 13时53分24秒
                                       
    #include <bits/stdc++.h>
    #define LL long long
    #define fir first
    #define sec second
    #define pii pair<int,int>
    using namespace std;
    const int MAXN = 2000000 + 2;
    const int P = (int)1e9 + 7;
    LL f[MAXN],g[MAXN];
    char str[MAXN];
    int pre[26];
    set<pii> rem;
    LL solve(int n,int k){
        int m = strlen(str + 1);
        memset(pre,0,sizeof(pre));
        f[0] = g[0] = 1;
        for(int i=1;i<=m;++i){
            int v = str[i] - 'a';
            if(pre[v] == 0)
                f[i] = g[i - 1];
            else
                f[i] = (g[i - 1] - g[pre[v] - 1] + P) % P;
            g[i] = (g[i - 1] + f[i]) % P;
            pre[v] = i;
    //        printf("i = %d f = %lld g = %lld
    ",i,f[i],g[i]);
        }
        rem.clear();
        for(int i=0;i<k;++i)
            rem.insert(pii(pre[i],i));
        for(int i=m+1;i<=m+n;++i){
    //        puts("ffff");
            pii now = *rem.begin();
            int pos = now.fir,v = now.sec;
            if(pos == 0)
                f[i] = g[i - 1];
            else
                f[i] = (g[i - 1] - g[pos - 1] + P) % P;
            g[i] = (g[i - 1] + f[i]) % P;
            pre[v] = i;
            rem.erase(rem.begin());
            rem.insert(pii(i,v));
        }
    //    for(int i=0;i<=m+n;++i)
    //        printf("i = %d f = %lld
    ",i,f[i]);
        return g[m + n];
    }
    int main(){
        int n,k;
        scanf("%d %d",&n,&k);
        scanf("%s",str + 1);
        printf("%lld
    ",solve(n,k));
        return 0;
    }
  • 相关阅读:
    RJ-45与RJ-48的区别
    PCB设计铜铂厚度、线宽和电流关系
    PCB设计铜铂厚度、线宽和电流关系
    WiFi天线对PCB布局布线和结构的要求详解
    WiFi模块选型参考
    掩码是什么意思?
    计算机音频基础-PCM简介
    使用CSS2对页面中的文字添加彩色边框
    SQL Plus
    A Famous Music Composer
  • 原文地址:https://www.cnblogs.com/-maybe/p/6254128.html
Copyright © 2020-2023  润新知