【NOIP2015】 Day2 T2 字串 (多维动归)

2018-09-12

原题传送门(洛谷)https://www.luogu.org/problemnew/show/P2679


模拟考试的时候完全没有想到 正确的DP方程呢 

本来写了一个大致是对的转移方程 结果算了一下空间 大概不够 就放弃了(第一维可以滚动 掉的啊喂 傻孩子啊) 愣是写了50分暴力

下面是五十分的原代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cstring>
 6 
 7 #define For(i,a,b) for(register int i=a;i<=b;++i)
 8 #define Dwn(i,a,b) for(register int i=a;i>=b;--i)
 9 #define Re register
10 
11 
12 using namespace std;
13 const int md=1e9+7;
14 
15 int na,nb,kx;
16 string As,Bs;
17 int sum=0;
18 char c[60];
19 int cf[60];
20 
21 int main(){
22 //    freopen("ex2.in","r",stdin);
23     freopen("substring.in","r",stdin);
24     freopen("substring.out","w",stdout);
25     cin>>na>>nb>>kx;
26     cin>>As; cin>>Bs;
27     if(kx==1){
28         int px=0;
29         while(1){
30             int p=As.find(Bs,px);
31             if(p==-1)break;
32             sum++; px=p+1;
33             if(sum>=md)sum%=md;
34         }
35         cout<<sum%md<<endl; return 0;
36     }
37     if(kx==2){
38         string s1,s2;
39         For(i,0,nb-2){
40             s1=""; s2="";
41             For(j,0,i)s1=s1+Bs[j];
42             For(j,i+1,nb-1)s2=s2+Bs[j];
43         //    cout<<s1<<" "<<s2<<" "<<sum<<endl;
44             
45             int px1,px2;
46             int sum2=0;
47             px1=px2=0;
48             
49             while(1){
50                 int p1=As.find(s1,px1);
51                 if(p1==-1)break;
52                 sum2=0;
53                 px2=p1+s1.size();
54                 if(p1>na-1)break;
55                 while(1){
56                     int p2=As.find(s2,px2);
57                     if(p2==-1)break;
58                     sum2++; px2=p2+1;
59                     if(sum2>=md)sum2%=md;
60                 }
61                 sum+=sum2;    if(sum>=md)sum%=md;
62                 px1=p1+1;
63             }
64         }
65         cout<<sum%md<<endl;
66         return 0;
67     }
68     if(kx==nb){
69         For(i,0,nb-1)c[i+1]=Bs[i],cf[i+1]=0;
70         For(i,0,na-1){
71             char ac=As[i];
72             Dwn(j,nb,1){
73                 if(ac==c[j]){
74                     if(j==1)cf[j]++;
75                     else cf[j]+=cf[j-1];
76                     cf[j]%=md;
77                 }
78             }
79         }
80         sum=cf[nb]%md;
81         cout<<sum<<endl;
82         return 0;
83     }
84 }

当k=1,(即只能分一个字串) 直接s.find()去搜就好了

当k=2,(即分两个字串)还是暴力用find()去搜

然后 当k=m (即与B串等长)想了一个挺巧妙的方法。。 用 cf[i] 数组表示此时的B串前 i 位的方案数  按顺序 考虑 A串的一个字符 

从后往前枚举 i  当 A[j]== B[i]  cf[i]+=cf[i-1]  (算了算了都是暴力 不看也罢


正确 方法 

多维动归 !

用我们 奥赛教练的话来说就是

        每次只要一考到 多维动归 我就替学生捏一把汗 这道题是能否拿到绝对高分的关键!!

然鹅 模拟测试时我没做出来呢

好了 下面正经写题解

DP数组  f[i][j][k][0/1] 表示A的前i位 B的前j位 用了k个字串 A的第i个字串是否使用的方案数

那么我们很容易就可以得到转移方程

   A[i]==B[j]

      1. 不取(0)f[i][j][k][0] <-- f[i-1][j][k][0] + f[i-1][j][k][1]

     2. 取  (1)f[i][j][k][1] <-- f[i-1][j-1][k][1] (与前方字符连成一个字串)+ f[i-1][j-1][k-1][1](自己单独另起一串)+ f[i-1][j-1][k-1][0]

  A[i]!=B[j]

      1. 不取(0)f[i][j][k][0] <-- f[i-1][j][k][0] + f[i-1][j][k][1]

      2. 取 显然只能等于 0

初始边界 所有的 f[i][0][0][0]=1 

然后愉快地把第一维 i 用滚动数组滚掉即可 

复杂度 O(nmk)

附上代码

 1 // luogu-judger-enable-o2
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstdlib>
 5 #include<cstring>
 6 #include<algorithm>
 7 
 8 #define For(i,a,b) for(register int i=a;i<=b;++i)
 9 #define Re register
10 using namespace std;
11 const int Na=1e3+10,Nb=210;
12 const int md=1e9+7;
13 long long f[2][Nb][Nb][2];
14 char A[Na],B[Nb];
15 int na,nb,kx;
16 int main(){
17 //    freopen("substring9.in","r",stdin);
18 //    freopen("substring.out","w",stdout);
19     scanf("%d%d%d",&na,&nb,&kx);
20     scanf("%s",A+1); scanf("%s",B+1);
21     f[0][0][0][0]=f[1][0][0][0]=1;
22     For(i,1,na){
23         int now=i%2;
24         For(j,1,nb) For(k,1,kx){
25             if(A[i]==B[j]){
26                 f[now][j][k][1]=(f[now^1][j-1][k][1]+f[now^1][j-1][k-1][1]+f[now^1][j-1][k-1][0])%md;
27                 f[now][j][k][0]=(f[now^1][j][k][1]+f[now^1][j][k][0])%md;
28             }else{
29                 f[now][j][k][1]=0;
30                 f[now][j][k][0]=(f[now^1][j][k][1]+f[now^1][j][k][0])%md;
31             }
32         }
33     }
34     long long fn=f[na%2][nb][kx][0]+f[na%2][nb][kx][1];
35     cout<<fn%md<<endl;
36     fclose(stdin); fclose(stdout);
37     return 0;
38     
39 }

猜你喜欢

转载自www.cnblogs.com/HLAUV/p/9727092.html
今日推荐