Loading

Leetcode 115. 不同的子序列(二维DP)

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

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

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

简单二维DP问题。设dp[i, j]表示以i结尾的子串含有t的长度为j的前缀的数量,二重循环遍历:

(1)s[i] == t[j]: 转移方程为\(dp[i][j] += dp[i - 1][j - 1] + dp[i - 1][j]\),含义是当前可以由两种情况转移过来:

​ (a) s[i]恰好就是t[j],s中以i - 1为结尾的子串包括t的长度为j - 1的前缀的情况数。

​ (b)s中以i - 1为结尾的子串已经包括整个t的长度为j的前缀的情况数。

(2)s[i] != t[j]: 转移方程为\(dp[i][j] += dp[i - 1][j]\),含义同上。

初始化需要处理所有的dp[i, 1]为当前已经出现过的t[1]的数量。(代码中其实没必要开cnt数组存储)

注意j初始化完了以后要从2开始,同时要开long long(虽然答案说范围在int32范围内)。还有一个坑点就是输入的字符串是大小写混合的。

#include <iostream>
using namespace std;
long long dp[1005][1005];//dp[i][j]表示以i结尾的子串含有t的长度为j的前缀的数量
long long cnt[100];
int lens, lent;
int main() {
	memset(cnt, 0, sizeof(cnt));
	memset(dp, 0, sizeof(dp));
	string s, t;
	cin >> s >> t;
	lens = s.size(), lent = t.size();
	s = " " + s, t = " " + t;
	for(int i = 1; i <= lens; i++) {
		cnt[s[i] - 'A']++;
		dp[i][1] += cnt[t[1] - 'A'];
	}
	for(int i = 1;i <= lens; i++) {
		for(int j = 2; j <= lent; j++) {//注意j初始化完了以后要从2开始
			if(s[i] == t[j]) {
				dp[i][j] += dp[i - 1][j - 1] + dp[i - 1][j];
			} else {
				dp[i][j] += dp[i - 1][j];
			}
		}
	}
	cout << dp[lens][lent];
	return 0;
}
posted @ 2021-03-17 16:56  脂环  阅读(62)  评论(0编辑  收藏  举报