分区dp求字符串在另外一个字符串中所有子序列形式的出现次数
举个栗子
假设有s1=abaaaba,我们希望求s2=ab的出现次数
那么我们可以使用这样的思路对ab进行分区间,也就是a,ab,b 分别代表了f[1,1] f[1,2] f[2,2]三个区间
当我们开始遍历s1时,可以得出a对这三个区间的影响
a:
得到结果
在只有a的情况下的结果
f[1 1] = 1 f[1 2] = 0 f[2 2]=0
b
现在有了ab情况下的结果
f[1 1]= 1 f[1 2] = 1 f[2 2] =1
大致的转移结果就是如上所述
那么如何来表示这个dp转移方程呢?
公式
为了方便计算,我们需要分别让 s 1 和 s 2 前面多一个空格字符,使得索引从 1 开始 假设 s 1 的长度为 n , s 2 的长度为 m ∑ i = 1 n ∑ j = 1 m ∑ k = j m ∑ c = j − 1 k − 1 ( d p [ i ] [ j ] [ k ] + = d p [ i − 1 ] [ j ] [ c ] ∗ d p [ i − 1 ] [ c + 2 ] [ k ] ∣ 当 s 1 [ i ] = = s 2 [ c + 1 ] 时 ) 为了方便计算,我们需要分别让s1和s2前面多一个空格字符,使得索引从1开始 \\ 假设s1的长度为n,s2的长度为m\\ \sum_{i=1}^{n} \sum_{j=1}^{m}\sum_{k=j}^{m}\sum_{c=j-1}^{k-1}(dp[i][j][k]+=dp[i-1][j][c]*dp[i-1][c+2][k]|当s1[i]==s2[c+1]时) 为了方便计算,我们需要分别让s1和s2前面多一个空格字符,使得索引从1开始假设s1的长度为n,s2的长度为mi=1∑nj=1∑mk=j∑mc=j−1∑k−1(dp[i][j][k]+=dp[i−1][j][c]∗dp[i−1][c+2][k]∣当s1[i]==s2[c+1]时)
大致意思就是给s2的区间,计算其数量情况。然后j到k表示了该区间的范围,当某个字符可以产生贡献时,其对该区间的影响就可以使用在这个字符之前的配对情况*在它之后的配对情况得到,对总区间的影响。
具体实现代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s;
string t;
cin>>s>>t;
int len1 = s.length();
int len2 = t.length();
s = ' '+s;
t = ' '+t;
int dp[len1+1][len2+2][len2+1];
//将每个区间前面的当作空集进行处理。
memset(dp,0,sizeof dp);
for(int i=0;i<len1+1;i++) for(int j=1;j<len2+2;j++) for(int k=0;k<j;k++)
dp[i][j][k] = 1;
for(int i=1;i<len1+1;i++)
for(int j=1;j<len2+1;j++)
for(int k=j;k<len2+1;k++)
{
///继承前面产生影响的区间情况
dp[i][j][k] = dp[i-1][j][k];
//当可以产生影响时,产生影响
for(int c=j-1;c<k;c++) if(s[i]==t[c+1])
{
dp[i][j][k] += dp[i-1][j][c]*dp[i-1][c+2][k];
}
else
{
dp[i][j][k] = dp[i-1][j][k];
}
}
cout<<dp[len1][1][len2];
}