zoukankan      html  css  js  c++  java
  • Intellectual Inquiry CodeForces

    题目链接:cf崩了,so。

    参考博客:https://blog.csdn.net/kyleyoung_ymj/article/details/51628238

    补题。

    题意:

    每个样例输入两行,第一行输入两个数字n,k,第二行输入一个字符串,你可以在第二行输入的字符串后面增加n个字符(k代表字符范围,比如k=2,那么你选择的字符有a,b;如果k=4,那么你可以选择的字符有a,b,c,d,就是从字符a开始的k个字符是可选择的)。你要使输入字符串在后面增加n个指定范围的字符之后得到的字符串的子序列数目最多,输出最大的子序列数量。记得还要包括一个空串。

    子序列是可以不连续的。

    仅供参考,错了请指正。

    这里我们假设用一个二维数组dp[i][j]表示到位置i(第i个字符)为止,以字符 j+'a' 结尾的的子序列数量。

    那么假设现在我们要在第i个位置上放一个字符 j ,那么我们应该更新dp[i][j]的值。

    假设sum表示的是所有的dp[i-1][j](0<=j<k)的和,那么dp[i][j]=(sum+1)%mod;

    这时的dp[i][j]可以分解为dp[i-1][j]+(sum-dp[i-1][j])+1;

    那么在位置i增加字符 j之后,就导致以字符 j结尾的子序列增加了(sum-dp[i-1][j])+1个了。

    其实就是所有不以 j 字符结尾的子序列数量加1,那为什么是增加这么多呢??

    我们来一个例子,现在在字符串aabac后面增加一个字符a,在加字符a之前,我们有以下子序列

    以a结尾的:a,aa,ba,aaa,aba,aaba;
    
    以b结尾的:b,ab,aab;
    
    以c结尾的:c,ac,bc,aac,abc,bac,aaac,aabc,abac,aabac;

    好,现在我们要在后面加一个字符 'a' 了,在这之前,我们不妨给上面的所有子序列再按照另一种分类来排列一遍(加了一个空串):

    1.空串,a,aa,aaa
    2.b,ba
    3.ab,aba
    4.aab,aaba
    5.c
    6.ac
    7.bc
    8.aac
    9.abc
    10.bac
    11.aaac
    12.aabc
    13.abac
    14.aabac

    这里的每一行的前面的字符串在增加字符'a'之后就变成了后面的字符串,也就是说前面的字符串是后面字符串的前缀。

    也就是说除了每一行的最后一个字符串(子序列)------>>它增加字符a之后会导致以a结尾的子序列增加1,前面的所有的子序列加上字符a之后得到的子序列实际上都是已经出现过的,即已经计算在dp[i-1][j]中了,而不是因为位置 i 上增加字符a之后带来的。上面的每一行都只会增加一个新的子序列。现在我们再来看上面加粗的那四行,这四行的第一个子序列要么不是以字符a结尾,要么就是空串(第一行有"空串"两个字......),那么我们是不是可以用第一个子序列来替代整个一行子序列,也可以说是用每行的第一个子序列来代替这一行的最后一个子序列,这样我们就去掉了所有以a结尾的子序列带来的影响,而将它转化为了所有不以字符a结尾的子序列带来的影响,当然,还包括一个空串。上面有14行,在字符串aabac后面增加一个字符a之后,增加的子序列就是14个。

    所以dp[i][j]=dp[i-1][j]+(sum-dp[i-1][j])+1。

    计算完dp[i][j]之后,这个时候sum实际上应该更新了,因为dp[i-1][j]变成了dp[i][j]

    此时 sum=(sum-dp[i-1][j]+dp[i][j])=sum-dp[i-1][j]+(sum+1)=2*sum-dp[i-1][j]+1;

    在我们增加字符 j 的时候,为了使sum的值最大,我们肯定是要挑一个dp[i-1][j]的值最小的啦,这个好像叫贪心...

    最后,对于已经给出的字符串,我们直接用上面的公式计算就可以了,而对于字符串后面要增加的n个字符,我们每次都找到一个字符 j ,它的dp[i-1][j]的值最小,这样根据上面的公式就可以使sum一直保持最大了,然后因为要取模,所以我们不可以直接比较大小,而是应该记录字符 'a' 到字符 'a '+k每一个字符最后出现的位置,找出现位置最小的那个字符加到字符串最后,因为位置最小的肯定是值最小的。

    代码(变成一维的了):

    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<map>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<set>
    #include<cstdio>
    #include<string>
    #include<deque> 
    using namespace std;
    typedef long long ll;
    #define eps 1e-8
    #define INF 0x3f3f3f3f
    #define maxn 2000005
    const int mod=1e9+7;
    ll n,m,k,t;
    char str[maxn];
    ll dp[26];//存以每一个字符结尾的子序列的数量 
    int last[26];//存每一个字符最后出现的位置 
    int main()
    {
        scanf("%lld%lld",&n,&k);
        scanf("%s",str+1);
        memset(last,0,sizeof(last));
        int len=strlen(str+1);
        ll sum=0;
        for(int i=1;i<=len;i++){//处理输入的字符串 
            int id=str[i]-'a';
            last[id]=i;//更新最后出现的位置 
            ll pre=dp[id];//把位置在i-1时的dp[id]暂时存一下 
            dp[id]=(sum+1+mod)%mod;//更新dp[id] 
            sum=(sum-pre+dp[id]+mod)%mod;//更新sum 
        }
        for(int i=1;i<=n;i++){//增加n个给定范围的字符 
            int Min=INF;// 找最小的位置 
            int id;//存位置最小的字符 
            for(int j=0;j<k;j++){
                if(last[j]<Min){
                    Min=last[j];
                    id=j;
                }
            }
            last[id]=i+len;//更新最后出现大的位置 
            ll pre=dp[id];//把位置在i-1时的dp[id]暂时存一下 
            dp[id]=(sum+1+mod)%mod;//更新dp[id] 
            sum=(sum-pre+dp[id]+mod*2)%mod;//更新sum 
        } 
        printf("%lld
    ",sum+1);//最后结果加1,因为还有一个空串 
        return 0;
    }
  • 相关阅读:
    SQL中Group By的使用
    SQL 触发器-如何查看当前数据库中有哪些触发器
    调试SQL Server的存储过程及用户定义函数
    SQL判断一个数是整数还是小数
    手动将Excel数据导入SQL
    SQL Case when 的使用方法
    相关资料
    三款大数据工具比拼,谁才是真正的王者
    SQL中CONVERT转化函数的用法
    Sq server 关于存储过程,触发器的一些理论简述
  • 原文地址:https://www.cnblogs.com/6262369sss/p/11986388.html
Copyright © 2011-2022 走看看