LeetCode 115. 不同的子序列

題目鏈接 LeetCode 115. 不同的子序列

題目描述

給定一個字符串 S 和一個字符串 T,計算在 S 的子序列中 T 出現的個數。

一個字符串的一個子序列是指,通過刪除一些(也可以不刪除)字符且不干擾剩餘字符相對位置所組成的新字符串。(例如,“ACE” 是 “ABCDE” 的一個子序列,而 “AEC” 不是)

題目數據保證答案符合 32 位帶符號整數範圍。

示例 1:
輸入:S = “rabbbit”, T = “rabbit”
輸出:3
解釋:

如下圖所示, 有 3 種可以從 S 中得到 “rabbit” 的方案。
(上箭頭符號 ^ 表示選取的字母)

rabbbit
^^^^ ^^
rabbbit
^^ ^^^^
rabbbit
^^^ ^^^
示例 2:

輸入:S = “babgbag”, T = “bag”
輸出:5
解釋:

如下圖所示, 有 5 種可以從 S 中得到 “bag” 的方案。
(上箭頭符號 ^ 表示選取的字母)

babgbag
^^ ^
babgbag
^^ ^
babgbag
^ ^^
babgbag
^ ^^
babgbag
^^^

解題思路

當初搞信息競賽的時候對DP掌握的就很差,現在得重新來過了,所以我們就從一開始的遞歸解法開始吧。

很多DP題目都可用遞歸來做,當然不能過掉全部的數據,但是我們可以用遞歸來找思路,然後再轉化成記憶化搜索,最後優化成DP,是一個很好的思路。

首先得搞清楚一個問題,不論是遞歸還是DP,都要搞清楚這個問題所涉及的所有狀態,哪些狀態是對提供答案有用的,哪些是沒用的,如果狀態不清楚的話就很難進行下一步操作。

在這道題中,我們先來找哪些狀態可以對提供答案有用。
①如果s[i]==t[j]
我們有兩種選擇
第一種即當前s[i]和t[j]相匹配,兩個指針都往下一個移動。
第二種就是不用當前的s[i]和t[j]匹配,而是用下一個s[i+1]和t[j]去匹配,因爲有可能出現 i 位置上和 i+1 位置上都是相同的,可以有選擇的用哪一個來匹配。

②如果s[i]!=t[j]
那麼 i 指針往下一個移動, j 指針不移動。

根據這幾個狀態我們就可以寫出遞歸的程序,進而也就可以寫出DP的程序代碼。

程序代碼

遞歸版

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int dfs(string s,string t,int i,int j) {
	if(j==t.size()) return 1;
	if(i==s.size()) return 0; 
	if(s[i]!=t[j]) return dfs(s,t,i+1,j);
	else if(s[i]==t[j]) return ( dfs(s,t,i+1,j) + dfs(s,t,i+1,j+1) ) ;
}
int main () {
	string s,t;
	cin>>s>>t;
	int ns=s.length();
	int nt=t.length();
	int ans=dfs(s,t,0,0);
	cout<<ans<<endl;
	return 0;
}

DP版

#include<cmath>
#include<cstdio>
using namespace std;
string s,t;
int main() {
	cin>>s>>t;
	int ns=s.length();
	int nt=t.length();
	int dp[ns+1][nt+1];
	for(int j=0;j<=nt;++j) dp[0][j]=0;
	for(int i=0;i<=ns;++i) dp[i][0]=1;
	for(int i=1;i<=ns;++i)
		for(int j=1;j<=nt;++j) 
			if(s[i-1]==t[j-1]) dp[i][j]=dp[i-1][j]+dp[i-1][j-1];
			else dp[i][j]=dp[i-1][j];
	cout<<dp[ns][nt]<<endl;

	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章