P2679 子串

段数之外还要记录当前段是否贴着当前位置

问题

P2679 子串
从字符串 AA 中选择恰好 kk 个互不重叠的非空子串,按原顺序拼接后得到字符串 BB。选择位置不同视为不同方案,求方案数对 109+710^9+7 取模后的结果。

数据规模:1≤n≤10001\le n\le1000,1≤m≤2001\le m\le200,1≤k≤m1\le k\le m。

最开始的状态只记录处理到 AiA_i、已经匹配 BB 的前 jj 位、使用 tt 段。但是这个状态无法判断下一次选择 Ai+1A_{i+1} 是继续当前段,还是在断开以后新开一段,说明缺少了影响转移的状态信息。

因此增加一维状态:0 表示当前 AiA_i 没有被选,1 表示 AiA_i 被选并且是当前最后一段的末尾。跳过 AiA_i 时,BB 的匹配长度与段数都不变;BjB_j 不能被跳过,它必须由某个真实选择的 AiA_i 产生。

当 Ai=BjA_i=B_j 时,状态 1 有三个来源:继续上一段;上一位没选并新开一段;上一位被选但在相邻位置主动切开,再新开一段。

于是就可以得到下面这版四维的 DP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ll f[maxn][maxm][maxk][2];
string A, B;

void solve( ) {
int n = read( ), m = read( ), k = read( );
cin >> A >> B;
A = ' ' + A, B = ' ' + B;

f[0][0][0][0] = 1;
for( int i = 1; i <= n; ++ i ) {
for( int j = 0; j <= m; ++ j ) {
for( int t = 0; t <= k; ++ t ) {
f[i][j][t][0] =
( f[i-1][j][t][0]
+ f[i-1][j][t][1] ) % mod;
if( j >= 1 && t >= 1 && A[i] == B[j] ) {
f[i][j][t][1] =
( f[i-1][j-1][t][1]
+ f[i-1][j-1][t-1][0]
+ f[i-1][j-1][t-1][1] ) % mod;
}
}
}
}
printf( "%lld\n", f[n][m][k][0] + f[n][m][k][1] );

}

由于状态空间太大了,于是滚动掉 ii 这一维。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ll f[maxm][maxk][2];
ll nf[maxm][maxk][2];
string A, B;

void solve( ) {
int n = read( ), m = read( ), k = read( );
cin >> A >> B;
A = ' ' + A, B = ' ' + B;

f[0][0][0] = 1;
for( int i = 1; i <= n; ++ i ) {
memset( nf, 0, sizeof nf );
for( int j = 0; j <= m; ++ j ) {
for( int t = 0; t <= k; ++ t ) {
nf[j][t][0] = ( f[j][t][0] + f[j][t][1] ) % mod;
if( j >= 1 && t >= 1 && A[i] == B[j] ) {
nf[j][t][1] = ( f[j-1][t][1] + f[j-1][t-1][0] + f[j-1][t-1][1] ) % mod;
}
}
}
memcpy( f, nf, sizeof nf );
}
printf( "%lld\n", ( f[m][k][0] + f[m][k][1] ) % mod );

}

P2679 子串
http://miqiu.space/posts/p2679001/
作者
Miqiu
发布于
2026年8月28日
许可协议