题目见:
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); } } };