LCS 問題

zhangyuyi1218發表於2024-06-21

LCS 問題

LCS,即最長公共子序列。

1. 樸素的求法

使用動態規劃,\(dp_{i,j}\) 代表以序列 \(a\)\(i\) 個字母結尾,序列 \(b\)\(j\) 個字母結尾的 LCS 長度。得動態轉移方程:

\[dp_{i,j} = \left\{\begin{matrix} \max(dp_{i-1,j},dp_{i,j-1}) & a_i \ne b_j\\ dp_{i-1,j-1} & a_i = b_j \end{matrix}\right. \]

Code1

#include <iostream>
using namespace std;
#define MAXN 10005
int f[MAXN][MAXN];
int a[MAXN],b[MAXN];
int main()
{
	int n;
	cin>>n;
	int m=n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=m;i++) cin>>b[i];
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			if(a[i]==b[j]) f[i][j]=f[i-1][j-1]+1;
			else f[i][j]=max(f[i][j-1],f[i-1][j]);
		}
	}
	cout<<f[n][m];
	return 0;
}

時間複雜度:\(O(n^2)\)

2. 最佳化處理

考慮使用 LIS 解決 LCS。

因為兩個序列都是 \(1 \sim n\) 的全排列,那麼兩個序列元素互異且相同,也就是說只是位置不同罷了,那麼我們透過一個 \(map\) 陣列將 \(a\) 序列的數字在 \(b\) 序列中的位置表示出來。

因為最長公共子序列是按位向後比對的,所以 \(a\) 序列每個元素在 \(b\) 序列中的位置如果遞增,就說明 \(b\) 中的這個數在 \(a\) 中的這個數整體位置偏後,可以考慮納入 LCS,那麼就可以轉變成時間複雜度為 \(O(n \log n)\) 的求用來記錄新的位置的 \(map\) 陣列中的 LIS!(程式碼展示的僅是洛谷 P1439 的程式碼,有一定侷限性【僅為 \(a,b\) 中沒有重複項時】)

Code2

#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>
using namespace std;
#define MAXN 100005 
int a[MAXN],b[MAXN],c[MAXN];
unordered_map<int,int> mp;
vector<int> vec;
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<n;i++) cin>>b[i];
	for(int i=0;i<n;i++) mp[a[i]]=i;
	for(int i=0;i<n;i++) c[i]=mp[b[i]];
	for(int i=0;i<n;i++)
    {
        int tem=lower_bound(vec.begin(),vec.end(),c[i])-vec.begin();
        if(tem==vec.size())
            vec.push_back(c[i]);
        else vec[tem]=c[i];
    }
    cout<<vec.size();
	return 0;
}

練習

  • P1439

相關文章