定长最小权值字符串个数-TopCoder-SRM586-DIV2-1000pt

题目见:

http://community.topcoder.com/stat?c=problem_statement&pm=12695&rd=15698

题目大意为,给定长度为L,计算所有权值最小的字符串的个数。权值定义为所有字母出现的最右位置-最左位置的加和。

容易想到的,若L<=26,那么显然每种字母只有一个时权值为0,最小,只要有字母重复就会大于0.所以此时字符串个数为C(26,1)*C(25,1)...C(26-L+1,1)个。

若L>26。则需要仔细思考怎样的字符串能够满足最小。

1)首先容易想到,26个字母必须都用上,不然能反证替换掉重复字符使权值更小。

2)对每个字母,其出现的位置必须是连续的,如果不是连续的,可以通过调整到连续使字符串更小。

所以以上两个条件给出了这种字符串长什么样: 1)每个字符出现至少一次。2)同一字符必须连续。

由此,可以通过构造的方法对这个计数问题进行DP或MEMO。

f (p , l): 已知字符串的[p+1,n),目前还有l个字符没有出现过,则[0,p]的字符串可能数是多少?

很容易对p位上的情况进行状态转移:

1) 使用跟p+1一样的字母。 res += f(p-1, l);

2) 重新选择一个字母填入当前位置。res += f(p-1,l-1) * C(l,1)

然后注意一些边界条件即可。

int m,n;
const int MODULAR=1000000009;
class StringWeightDiv2
        { 
        public: 
            int comb[27][27];
            int dp[1001][27];
            //clac the combination use C(a,b)=C(a-1,b-1)+C(a-1,b)
            int CalcC(int n,int m){
                if (n==0 && m==0){return 1;}
                if (n==0){return 0;}
                if (m==0){return 1;}
                if (n<m){return 0;}
                if (comb[n][m]!=-1){return comb[n][m];}
                int res=(CalcC(n-1,m-1)+CalcC(n-1,m))%MODULAR;
                comb[n][m]=res;
                return res;
            }
            //how many ways for [0,p] with l charectors unused
            int Solve(int p,int l){
                if (p==-1){return l==0;}
                if (dp[p][l]!=-1){return dp[p][l];}
                long long res=0;
                if (l>0){    //begin a new consecutive seria
                    res+=(long long)Solve(p-1,l-1)*CalcC(l,1);
                    res%=MODULAR;
                }
                if (l<26){    //continue with last charactor
                    res+=Solve(p-1,l);
                    res%=MODULAR;
                }
                dp[p][l]=(int)res;
                return (int)res;
            }
        int countMinimums(int L) 
            { 
                memset(comb,-1,sizeof(comb));
                if (L<=26){        //just select unique charactors in the permutation
                    long long res=1;
                    for (int i=0;i<L;i++){
                        res*=CalcC(26-i,1);
                        res%=MODULAR;
                    }
                    return res;
                }
                else{
                    //when L>26, the string should meet:
                    //1)use all the 26 characters
                    //2)for one specify character, its appearance must be consecutive.
                    memset(dp,-1,sizeof(dp));
                    return Solve(L-1,26);
                }
            } 
};

 

转载于:https://www.cnblogs.com/yangsc/p/3988349.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值