题目链接:http://codeforces.com/contest/655/problem/E
大意是Bessie只会英文字母表中的前k种字母,现在有一个长度为m+n的字母序列,Bessie已经知道了前m个字符,问如何填充剩下的n个字符,使得整个序列的不同子序列数目最大。当然所有字母都得是Bessie会的前k个字母。
两个月前比赛的时候做的,是一道不错的题。
关于子序列个数的计算
令dp[i]表示前i个数字组成的序列中子序列的个数,
则对于第i个数字a[i]来说,dp[i]来源于两种情况的转化:
序列中前一个数字为a[i],记这种情况为s1以及前一个数字不为a[i],计这种情况为s2。
dp[i]=s1+s2*2=2*dp[i-1]-s1
可以这么理解,首先之前的子序列个数是dp[i-1],这些子序列直接都加一个a[i],一定是可以的。这只是新答案中以a[i]为结尾的情况,那么不以a[i]为结尾的情况,直接就是继承自原来的s2。
所以可以看出要使得dp值尽量大,就要让每一次因为重复需要减去的s1值尽可能小,也就是要让a[i]距离上一次出现a[i]尽可能地远。
根据前m个字符信息,计算可以使用的k个字母的最后出现的位置,根据这个来进行排序,使得最后出现的位置越小的字母排在越前面。
对于剩下的n个需要填充的字符,直接按照排出来的k个字母顺序,循环使用。
最后按照上述子序列计算方式来进行计算。
1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <math.h> 8 #include <queue> 9 #include <stack> 10 #include <map> 11 #include <set> 12 13 using namespace std; 14 15 16 const int N=2234567; 17 18 const long long MOD=1e9+7; 19 20 char s[N]; 21 struct Node { 22 char ch; 23 int pos; 24 Node(){ 25 pos=-1; 26 } 27 bool operator < (const Node &o) const { 28 return pos<o.pos; 29 } 30 }node[30]; 31 long long cnt[30]; 32 int main() { 33 for (int i=0;i<26;i++) 34 node[i].ch=char('a'+i); 35 int n,k; 36 scanf("%d %d",&n,&k); 37 scanf("%s",s); 38 int m=strlen(s); 39 for (int i=0;i<m;i++) { 40 int id=s[i]-'a'; 41 node[id].pos=i; 42 } 43 sort(node,node+k); 44 for (int i=m,cnt=0;i<m+n;i++,cnt++) { 45 if (cnt>=k) cnt=0; 46 s[i]=node[cnt].ch; 47 } 48 s[m+n]=0; 49 //puts(s); 50 long long ret=1; 51 memset(cnt,0,sizeof cnt); 52 for (int i=0;i<m+n;i++) { 53 long long tmp=ret; 54 int id=s[i]-'a'; 55 ret=ret*2-cnt[id]; 56 ret+=MOD; 57 ret%=MOD; 58 while (ret<0) ret+=MOD; 59 cnt[id]=tmp; 60 } 61 printf("%I64d ",ret); 62 return 0; 63 }