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 }