原题链接

题目翻译(出自洛谷AT_abc130_e [ABC130E])

给出两个长度分别为N和M的整数序列S和T,它们均由 \(1\)\(10^5\)(含 \(10^5\) )之间的整数组成。

求在S子序列和T子序列中,有多少对两个子序列的内容相同。

子序列的说明:

A的子序列是指通过从A删除零个或多个元素而不改变顺序而获得的序列。

对于S和T而言,如果子序列的内容相同,但是被删除元素的索引集(位置)不同,也当成两个不同的子序列。

输出答案模 109+7 的结果

分析

就本人少之又少的线性dp刷题量而言,这种涉及公共子序列的问题,转移方程的条件一般都是 \(s_i = t_ j\) 转移状态一般都是两个序列的前 \(i\) 和前 \(j\) 元素,即 \(f_{i, j}\) 表示 \(s[: i]\)\(t[: j]\) 的公共部分,在此题即 \(s[: i]\)\(t[: j]\)的相同子序列个数。
\(P(s_x)\)\(s[:x-1]\)增加 \(s _x\)\(f _{x-1, y-1}\) 的影响,同理 \(t _y\)
\(P(s_x,t_y)\)\(s[:x-1]\)\(t[:y-1]\) 增加 \(\{s _x\)\(t _y\}\) 对对 \(f _{x-1, y-1}\) 的影响。

容易想到 \(f _{i, j - 1} - f _{i -1 , j -1} = P(s _i)\),同理 \(t _j\)
\(s _i=t _j \rightarrow P(s _i, t _j) = 2 \times f _{i-1, j-1}\)
\(s _i\neq t _j \rightarrow P(s _i, t _j) = 0\)
$ f _{i, j} = P(s _i) + P(t _j) + P(s _i, t _j)$
所以转移方程

\[\begin{cases} f_{i,j} = f_{i, j-1}+f_{i-1, j} ,\space s_i = t_j\\ f_{i,j} = f_{i, j-1}+f_{i-1, j}+f_{i-1, j-1},\space s_i\neq t_j\\ \end{cases} \]

代码实现

#include<iostream>
#include<cmath>
#include<string>
using namespace std;

const int N = 5e3 + 10;
const int mod = 1e9 + 7;


int n,m;
long long f[N][N];
int ans = 0;
int s[N], t[N];


int main() {
	cin >> n >> m; 
	for (int i = 1; i <= n; i++) cin >> s[i];   
	for (int j = 1; j <= m; j++) cin >> t[j];
	f[0][0] = 1;
	for (int i = 1; i <= n; i++) f[i][0] = 1;
	for (int j = 1; j <= m; j++) f[0][j] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= m; j++) {   
			if (s[i] == t[j]) {
				f[i][j] = (f[i - 1][j] + f[i][j - 1]) % mod;
			}
			else f[i][j] = (f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + mod) % mod;
		}
	}

	cout << f[n][m] << endl;
	
}