【NOIP2015提高组】子串 区间DP+滚动数组优化

题意:

有两个仅包含小写英文字母的字符串 A 和 B。

现在要从字符串 A 中取出 k 个互不重叠的非空子串,然后把这 k 个子串按照其在字符串 A 中出现的顺序依次连接起来得到一个新的字符串。

请问有多少种方案可以使得这个新串与字符串 B 相等?

注意:子串取出的位置不同也认为是不同的方案。

数据范围:

对于第 1 组数据:1≤n≤500,1≤m≤50,k=1;
对于第 2 组至第 3 组数据:1≤n≤500,1≤m≤50,k=2
对于第 4 组至第 5 组数据:1≤n≤500,1≤m≤50,k=m
对于第 1 组至第 7 组数据:1≤n≤500,1≤m≤50,1≤k≤m
对于第 1 组至第 9 组数据:1≤n≤1000,1≤m≤100,1≤k≤m
对于所有 10 组数据:1≤n≤1000,1≤m≤200,1≤k≤m

------------------------------------------------------我是分割线----------------------------------------------------

题解:一道状态不太明显的题,我们首先要寻找这道题的切入点。

观察这道题的特点,我们发现,相对于B,A中的每个字符显然有选与不选两种决策。

而对于这两个字符串,可以用两个指针表示,一个指向A的状态,一个指向B的状态。

于是这道题的状态就很被发现了。

设置状态:

设 F(i,j,k,1) 表示A的前i个字符中,匹配到B中当前第j个字符,一共使用了k段,选择当前第i个字符的方案数。

F(i,j,k,0)  表示A的前i个字符中,匹配到B中当前第j个字符,一共使用了k段,不选择当前第i个字符的方案数。

然后就是状态转移:

由状态,我们可以推出如下状态转移方程:

(1) 当Ai == Bj 时,F(i,j,k,0) = F(i-1,j,k,0)+ F(i-1,j,k,1).

     即当前A中第i个数不作为答案的方案数 == 前i-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);

     即当前A中第i个数作为答案的方案数 == A中前i-1个数匹配到B中第j-1个的方案数总和(i不接到前一个答案中) + A中前i-1个数方案(i接到前一个答案中)。  

(2)当Ai  != Bj 时, F(i,j,k,0) = F(i-1,j-1,k,0) + F(i-1,j-1,k,1).

             同(1)中的转移。

           F(i,j,k,1) = 0;  

          由于当前数不能作为答案,所以方案为0.

再确定边界条件,我们可以知道,F(i,0,0,0) = 1;即匹配B中0个字符的方案为1.

于是我们可以写出代码:

#include<bits/stdc++.h>

#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)

using namespace std;

typedef pair<int, int> pii;
typedef double db;
const int mod = 1e9+7;
const int N = 1e6 + 50;
int n, m, p;
int f[1010][101][101][2];
char a[N], b[N];
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
    while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
    return x*f;
}
void work(){
    rep(i, 0, n) f[i][0][0][0] = 1;
    rep(i, 1, n) rep(j, 1, m) rep(k, 1, p){
        if(a[i] == b[j]) {
            f[i][j][k][0] = (f[i-1][j][k][0] + f[i-1][j][k][1])%mod;
            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]) % mod) % mod;
        }
        else {
            f[i][j][k][1] = 0;
            f[i][j][k][0] = (f[i-1][j][k][0] + f[i-1][j][k][1])%mod;
        }
    }
    printf("%d
", (f[n][m][p][1] + f[n][m][p][0])%mod); 
}
void init(){
    n = read(); m = read(); p = read();
    scanf("%s%s", a+1, b+1);
}
int main(){
    init(); 
    work();
    return 0;
}
View Code

但是我们发现,这份代码空间复杂度效率低下(2*n*m*k),无法通过此题,我们还需要优化。

于是乎,DP常用的空间优化:滚动数组优化就出现了。

观察DP转移方程,我们可以发现,每一个决策i只与前一个决策i-1有关,其他的空间都是多余的。

所以我们就可以用01方法表示。

AC代码如下:

#include<bits/stdc++.h>

#define ll long long
#define mp make_pair
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)

using namespace std;

typedef pair<int, int> pii;
typedef double db;
const int mod = 1e9 + 7;
const int N = 1e6 + 50;
int n, m, p;
int f[2][220][220][2];
char a[N], b[N];
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9') { if(ch == '-') f = -1; ch = getchar();}
    while(ch >='0' && ch <='9') { x = (x<<3)+(x<<1)+(ch^48); ch = getchar();}
    return x*f;
}
void work(){
    int val = 1;
    f[0][0][0][0] = f[1][0][0][0] = 1; 
    rep(i, 1, n) {
        rep(j, 1, m) rep(k, 1, p){
            if(a[i] == b[j]) {
                f[val][j][k][0] = (f[val^1][j][k][0] + f[val^1][j][k][1])%mod;
                f[val][j][k][1] = (f[val^1][j-1][k-1][0] + (f[val^1][j-1][k-1][1] + f[val^1][j-1][k][1]) % mod) % mod;
            }
            else {
                f[val][j][k][1] = 0;
                f[val][j][k][0] = (f[val^1][j][k][0] + f[val^1][j][k][1])%mod;
            }
        }
        val ^= 1;
    }
    printf("%d
", (f[n&1][m][p][1] + f[n&1][m][p][0])%mod); 
}
void init(){
    n = read(); m = read(); p = read();
    scanf("%s%s", a+1, b+1);
}
int main(){
    init(); 
    work();
    return 0;
}
View Code

总结:这道题作为线性DP的练习题(NOIP的题),有一定的思维难度,对DP思维提升有很大的帮助。

原文地址:https://www.cnblogs.com/smilke/p/11587313.html