P2679 [NOIP2015 提高组] 子串
考察:线性dp
此题不会,fw本f
思路:
求方案数且需要取模,基本上使用dp求解.
根据题目,dp状态一定要记录a取到哪一位,b匹配到哪一位,同时还有段数限制,所以还需要记录段数.所以设置f[i][j][k]为以a的前i位匹配了b前j位,已经用了k段.
此时还发现对于第i位,我们需要判断它是否是与前面构成连续的一段,而且还需要判断第i位是否使用.
那我们再增加一维f[i][j][k][0/1]表示第i位是否使用,就可以区分是否使用和是否连续的四种情况.
再考虑初始化,根据dp定义f[0][0][0][0] = 1但也有前i位都不使用的状态因此f[i][0][0][0] = 1
1 if(a[i]==b[j]) f[i][j][k][0] = f[i-1][j][k][0]+f[i-1][j][k][1] f[i][j][k][1] = f[i-1][j-1][k-1][0]+f[i-1][j-1][k-1][1]+f[i-1][j-1][k][1] 2 3 else f[i][j][k][0] = f[i-1][j][k][1]+f[i-1][j][k][0]
如果直接这么写需要1000*200*200*2大概300MB的空间,因此需要空间优化.观察到只需要i和i-1.所以能滚动数组.只需要在此基础上在第i位&1即可.但是注意,当我们不滚动的时候,每次v与t的值都是0,所以滚动的时候需要重新赋值0.
1 #include <iostream> 2 #include <cstdio> 3 using namespace std; 4 typedef long long LL; 5 const int N = 1010,M = 210,Mod = 1000000007; 6 int n,m,k,f[2][M][M][2]; 7 char a[N],b[M]; 8 void solve() 9 { 10 f[0][0][0][0] = 1;//记录方案的数组滚动需要清零 11 f[1][0][0][0] = 1; 12 for(int i=1;i<=n;i++) 13 { 14 f[i&1][0][0][0] =1; 15 for(int j=1;j<=m;j++) 16 for(int s=1;s<=k;s++) 17 { 18 int& v = f[i&1][j][s][1]; 19 int& t = f[i&1][j][s][0]; 20 v = t = 0; 21 if(a[i]==b[j]) 22 { 23 int val = ((LL)f[i-1&1][j-1][s-1][0]+f[i-1&1][j-1][s-1][1])%Mod; 24 val = ((LL)val+f[i-1&1][j-1][s][1])%Mod; 25 v = ((LL)v+val)%Mod; 26 } 27 t += ((LL)f[i-1&1][j][s][1]+f[i-1&1][j][s][0])%Mod; 28 t%=Mod; 29 v%=Mod; 30 } 31 } 32 } 33 int main() 34 { 35 scanf("%d%d%d%s%s",&n,&m,&k,a+1,b+1); 36 solve(); 37 int ans = ((LL)f[n&1][m][k][1]+f[n&1][m][k][0])%Mod; 38 printf("%d\n",ans); 39 return 0; 40 }