P4303 [AHOI2006]遺伝子マッチング
タイトルリンク:https : //www.luogu.com.cn/problem/P4303
効果は、 n個を与え、長さ5Nの二つの配列、これら二つの配列、1-nは、それぞれ数は5回、最長共通部分列を見つけることが表示されます。
入力例
2
1 1 1 1 1 2 2 2 2 2
1 1 1 2 2 2 2 2 1 1
出力例
8
分析
nは最大20000、lcを求める通常の方法ではtになります。
これら2つのシーケンスの特殊性に注意してください。多くの繰り返し要素があります。
各要素の位置を記録できます。例として栗を取り上げます。
シーケンス1の場合:1が出現する場所:1 2 3 4 5 2が出現する場所6 7 8 9 10
シーケンス2の要素をそれらの位置に置き換えます。
(1 2 3 4 5)(1 2 3 4 5)(1 2 3 4 5)(6 7 8 9 10)(6 7 8 9 10)(6 7 8 9 10)(6 7 8 9 10)( 6 7 8 9 10)(1 2 3 4 5)(1 2 3 4 5)
位置を逆の順序に変更します(理由):
(5 4 3 2 1)(5 4 3 2 1)(5 4 3 2 1)(10 9 8 7 6)(10 9 8 7 6)(10 9 8 7 6)(10 9 8 7 6)( 10 9 8 7 6)(5 4 3 2 1)(5 4 3 2 1)
lisにこのことを尋ねてください。nlognのアルゴリズムがあります。
原理は何ですか?
シーケンス2の位置は、シーケンス1の要素に対応する昇順を満たす必要があります。
そして、位置を逆の順序に変更して、各ポイントが一度だけ取得されるようにしました
コード
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn = 1000000;
int n, x, tot, a[maxn], low[maxn];
vector<int> v[maxn];//v[i]存储i可能的位置
signed main(){
scanf("%d", &n);
for(int i=1; i<=5*n; i++){
scanf("%d", &x);
v[x].push_back(i);记录位置
}
for(int i=1; i<=5*n; i++){
scanf("%d", &x);
for(int j=4; j>=0; j--){
a[++tot] = v[x][j];//倒序把顺序存入a
}
}
low[1] = a[1];
int ans = 1;
for(int i=2; i<=tot; i++){//对a求lis,不多说
if(a[i] > low[ans]) low[++ans] = a[i];
else{
int x = lower_bound(low+1, low+1+ans, a[i]) - low;
low[x] = a[i];
}
}
printf("%d\n", ans);
return 0;
}