省选前集训 区间dp

字符串统计

有n 个不同的字符串都只包含小写字母,它们都由字典序排序后,并且满足

s1<s2<s3<...<sn。然而,有些字符看不见了,我们用?代替,那么有多少种可

能满足给定的条件?

 

输入:

第一行包含一个n 表示字符串个数

接下来n 行顺序从s1 到s2 到...到sn,n 个字符串只由小写字母和问号组成。

 

对于20%的数据n<=2,每个字符串长度不超过2

对于100%的数据n<=50,每个字符串长度不超过20

 

因为区间外的字符串顺序互补影响,所以考虑区间dp

dp[l][r][p][c]表示区间l,r;每个字符串考虑到p位置,可以填>=c的字符,直接转移。

因为前面的位决定以后就不需要再考虑前面的了,只需知道现在可以填哪些字符

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 #define mod 1000000007
 7 typedef long long LL;
 8 
 9 char ch[100][100];
10 int n;
11 const char mn = char('a' - 1),mx = 'z';
12 int dp[100][100][50][50];
13 
14 int DP(int l,int r,int p,int c){
15     if ( c > mx ) return 0;
16     if ( p > 20 ) return l == r;
17     int &d = dp[l][r][p][c];
18     if ( d != -1 ) return d;
19     d = 0;
20     d = DP(l,r,p,c + 1);
21     for (int i = l ; i <= r ; i++){
22         if ( ch[i][p] != '?' && ch[i][p] != c ) break;
23         if ( ch[i][p] == '?' && c == mn ) break;
24         int v = DP(l,i,p + 1,mn);
25         if ( i < r ) v = ((LL)v * (LL)DP(i + 1,r,p,c + 1)) % mod;
26         d = (d + v) % mod;
27     }
28     return d;
29 }
30 int main(){
31     freopen("a.in","r",stdin);
32     freopen("a.out","w",stdout);
33     scanf("%d",&n);
34     memset(dp,-1,sizeof(dp));
35     for (int i = 1 ; i <= n ; i++){
36         scanf("%s",ch[i] + 1);
37         int l = strlen(ch[i] + 1);
38         for (int j = l + 1 ; j <= 20 ; j++){
39             ch[i][j] = mn;
40         }
41     }
42     printf("%d\n",DP(1,n,1,mn));
43 }

 

多总结dp的方法,从菜鸡慢慢变强!

posted @ 2016-03-06 21:28  zhangqingqi  阅读(143)  评论(0编辑  收藏  举报