分区dp求字符串在另外一个字符串中所有子序列形式的出现次数

分区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]时) 为了方便计算,我们需要分别让s1s2前面多一个空格字符,使得索引从1开始假设s1的长度为n,s2的长度为mi=1nj=1mk=jmc=j1k1(dp[i][j][k]+=dp[i1][j][c]dp[i1][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];
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值