leecode 115 不同的子序列 对子串包含问题的思考与理解

前言:

在之前的LDU训练赛和UPC训练赛中都出现了字串包含问题,在我前面的两篇博客中也介绍了那两个题的思路,但是这两个题有一个共同的特点,那就是他们要寻找的字串都是提前已知的,如果给出任意两个串,寻找A串中B字串的个数,之前我的思路显然不可用,因此我找到了力扣的“不同的子序列”这个问题,进行学习;

题目描述:

给定一个字符串 s 和一个字符串 t ,计算在 s 的子序列中 t 出现的个数。

字符串的一个 子序列 是指,通过删除一些(也可以不删除)字符且不干扰剩余字符相对位置所组成的新字符串。(例如,“ACE” 是 “ABCDE” 的一个子序列,而 “AEC” 不是)

题目数据保证答案符合 32 位带符号整数范围。

来源:力扣(LeetCode)
链接:不同的子序列
提示:
0 <= s.length, t.length <= 1000
s 和 t 由英文字母组成

思路:
这题我们用动态规划来做

1.当s.length<t.lenth 时不存在子序列
2.当s.length>=t.lenth 时

设二维数组dp[ i ][ j ] 表示 s[ :i ] 中 字串 t[ :j ] 出现的个数

s[ :i ]表示 s 串从 0 到 i 的部分
t[ :j ] 表示 t 串 从0 到 j 的部分

我们首先进行预处理:
当 j = 0 时,s串无论取任意长度,空串都是它的字串

for(int i=0;i<=lena;i++) dp[i][0]=1;

当 i = 0 时 t串无论取任意长度,s不可能含有字串

for(int i=1;i<=lenb;i++) dp[0][i]=0;

特别注意 dp[ 0 ][ 0 ] = 1;
不要在第二次赋值时把这个值覆盖掉;

1.当s[ i ] = t[ j ] 时
s[ i ] 自己可以选择自己是否与 t[ j ] 匹配
若s[ i ] 与 t[ j ]匹配 那么考虑 t[ :j -1] 做 s[ :i-1 ] 的字串
dp[ i ][ j ] =dp[ i -1][ j-1 ]
若s[ i ] 与 t[ j ]不匹配 那么考虑 t[ :j ] 做 s[ :i-1 ]的字串
dp[ i ][ j ] =dp[ i -1][ j ]
2.当s[ i ] != t[ j ] 时
s[ i ] 不可以于 t[ j ] 匹配
那么考虑 t[ :j ] 做 s[ :i-1 ]的字串
dp[ i ][ j ] =dp[ i -1][ j ]

因此转移方程:

for(int i=1;i<=lena;i++)
	{
		for(int j=1;j<=lenb;j++)
		{
			if(a[i-1]==b[j-1])
				dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
			else 
				dp[i][j]=dp[i-1][j];
		}
	}

最终代码:

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const ll maxx = 1e18;
const int N = 1e5+100;
const int p = 1e4;
const double eps = 1e-8;

string a,b;
ull dp[1000][1000];// a[:i]子序列中   b[:j]子序列出现的次数  
int lena,lenb;


int main()
{
	cin>>a;
	cin>>b;
	lena=a.size();
	lenb=b.size();
	
	if(lena<lenb)
	{
		cout<<"0";
		retrun 0;
	}
	
	for(int i=0;i<=lena;i++) dp[i][0]=1;
	for(int i=1;i<=lenb;i++) dp[0][i]=0;
	
	for(int i=1;i<=lena;i++)
	{
		for(int j=1;j<=lenb;j++)
		{
			if(a[i-1]==b[j-1])
				dp[i][j]=dp[i-1][j-1]+dp[i-1][j];
			else 
				dp[i][j]=dp[i-1][j];
		}
	}
	
	cout<<dp[lena][lenb];	
} 

数据加强版:

如果仅仅是一个动态规划没什么意思,但是如果这个题加强一下数据变成
0 <= s.length, t.length <= 2000,那么我想即使是 unsigned long long 也放不下最终的结果,这时候就需要高精度的帮助

#include<bits/stdc++.h>
using namespace std;
 
typedef unsigned long long ull;
typedef long long ll;
 
const ll maxx = 1e18;
const int N = 1e5+100;
const int p = 1e4;
const double eps = 1e-8;

string a,b;
string dp[2000][2000];// a[:i]子序列中   b[:j]子序列出现的次数  
int lena,lenb;

string add(string s1,string s2)
{
	int len1=s1.size();
	int len2=s2.size();
	string ss="";
	int a[N],b[N];
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	
	
	for(int i=0;i<len1;i++) a[i+1]=s1[len1-1-i]-'0';
	for(int i=0;i<len2;i++) b[i+1]=s2[len2-1-i]-'0';
	int l=1,r=1,k=1,carry=0;
	while(l<=len1||r<=len2)
	{
		b[k]=a[l]+b[r]+carry;
		carry=b[k]/10;
		b[k]%=10;
		l++;r++;k++;
	}
	
	while(carry)
	{
		b[k]=carry%10;
		carry/=10;
		k++;
	}
	
	for(int i=k-1;i>=1;i--) ss+=(b[i]+'0');
	
	return ss;
}

int main()
{
	cin>>a;
	cin>>b;
	lena=a.size();
	lenb=b.size();
	
	if(lena<lenb)
	{
		cout<<"0";
		retrun 0;
	}
	
	
	for(int i=0;i<=lena;i++) dp[i][0]="1";
	for(int i=1;i<=lenb;i++) dp[0][i]="0";
	
	for(int i=1;i<=lena;i++)
	{
		for(int j=1;j<=lenb;j++)
		{
			if(a[i-1]==b[j-1])
				dp[i][j]=add(dp[i-1][j-1],dp[i-1][j]);
			else 
				dp[i][j]=dp[i-1][j];
		}
	}
	
	cout<<dp[lena][lenb];
} 

注意:易出错的地方在于高精的时候存入数组处理后是倒序的,所以放到一个新串中要倒着处理,而且用两个数组处理高精而不用三个,节省时间空间不易爆内存

之前我也遇到过一个 dp+高精 的问题,没做出来很可惜,这次又找到了一个类似的问题,很高兴解决了,加油!;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QiWen.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值