首页 > 代码库 > 最长上升子串
最长上升子串
n^2的解法
对于a[i]来说,j in 0..i-1,如果a[i]>a[j],则dp[i] = dp[j]+1,否则dp[i]至少为1
For i in 0..n For j in 0..i-1 If(a[i] > a[j]) dp[i] = max(dp[i],dp[j]+1); dp[i] = max(1,dp[i]);
nlogn解法
http://www.ahathinking.com/archives/117.html
目的:我们期望在前i个元素中的所有长度为len的递增子序列中找到这样一个序列,它的最大元素比arr[i+1]小,而且长度要尽量的长,如此,我们只需记录len长度的递增子序列中最大元素的最小值就能使得将来的递增子序列尽量地长。
方法:维护一个数组MaxV[i],记录长度为i的递增子序列中最大元素的最小值,并对于数组中的每个元素考察其是哪个子序列的最大元素,二分更新MaxV数组,最终i的值便是最长递增子序列的长度。这个方法真是太巧妙了,妙不可言。
MaxV[ ]必然是一个递增的数组,记录长度为i的序列中最大的那个数(也就是最后的那个数)为多少
对于a[] = { 1,-1, 2,-3,4,-5, 6,7} 这个例子来说,
i为1时,一开始MaxV[0] = 1;
i为2时,然后MaxV[0] = -1,因为 -1 < 1,用-1来替换1可以取得更好的结果
i为3时,MaxV[1] = 2,因为 2 > -1,所以 2可以接在-1的后面组成一个更长的数组
i为4时,MaxV[0] = -3,因为 -3 < -1 ,用-3来替代-1可以取得更好的效果。
。。。。。。。。
所以要是能够快速在MaxV[]中刚好比a[i]大的数k,然后用a[i]来替换k就可以取得更好的效果,如果MaxV[]中所以的数都比a[i]小,那么说明可以得到一个更长的序列,就把MaxV[++len] = a[i]
for i in 0..n do
{
if(a[i] > MaxV[len-1]) /* 大于最大的自然无需查找,否则二分查其位置 */ MaxV[len++] = a[i]; else { int pos = BinSearch(MaxV,len,a[i]);//相当于lower_bound, 返回 MaxV[i]中刚刚大于x的那个元素的下标 MaxV[pos] = a[i]; }
}
以下是几种详细的代码
#include <iostream>using namespace std;/* 最长递增子序列 LIS * 设数组长度不超过 30 * DP + BinarySearch*/int MaxV[30]; /* 存储长度i+1(len)的子序列最大元素的最小值 */int len; /* 存储子序列的最大长度 即MaxV当前的下标*//* 返回MaxV[i]中刚刚大于x的那个元素的下标 */int BinSearch(int * MaxV, int size, int x){ int left = 0, right = size-1; while(left <= right) { int mid = (left + right) / 2; if(MaxV[mid] <= x) { left = mid + 1; }
else { right = mid - 1; } } return left;}int LIS(int * arr, int size){ MaxV[0] = arr[0]; /* 初始化 */ len = 1; for(int i = 1; i < size; ++i) /* 寻找arr[i]属于哪个长度LIS的最大元素 */ { if(arr[i] > MaxV[len-1]) /* 大于最大的自然无需查找,否则二分查其位置 */ { MaxV[len++] = arr[i]; }
else { int pos = BinSearch(MaxV,len,arr[i]);//相当于lower_bound, 返回MaxV[i]中刚刚大于x的那个元素的下标 MaxV[pos] = arr[i]; } } return len;} void main(){ int arr[] = {1,-1,2,-3,4,-5,6,-7}; /* 计算LIS长度 */ printf("%d\n",LIS(arr,sizeof(arr)/sizeof(int)));}
使用lower_bound函数
Foj 1438实在是太恶心了。。。。 #include<stdio.h>#include<stdlib.h>#include<iostream>#include<string.h>#include<queue>#include<algorithm>using namespace std;#define LL __int64#define M 220100const int INF = 2139062143;const int mod = 1000000007;int __ = 0;#define BIAOJI printf("yes%d\n",++__); int f[M] = {0};int a[M] = {0},n,nb = 0;int b[M] = {0}; int main(){ freopen("in.txt","r",stdin); while(cin>>n) { memset(f,0,sizeof(f)); memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(int i = 1;i<=n;++i)scanf("%d",&a[i]); f[1] = 1; b[1] = a[1]; nb = 1; for(int i = 2;i<=n;++i) { int kk = lower_bound(b+1,b+nb+1,a[i]) - b; if(kk == nb+1) ++nb; b[kk] = a[i]; f[i] = kk; //PP(); } int ret = 0; for(int i = 1;i<=n;++i) ret = max(ret,f[i]); cout<<ret<<endl; } return 0; } /*int main(){ freopen("in.txt","r",stdin); while(cin>>n) { int ans = 0; memset(a,0,sizeof(a)); for(int i = 0;i<n;++i)scanf("%d",&a[i]); int *b=new int [n+1]; b[ans=0]=-0x7fffffff; for(int i=0;i<n;i++){ int k=lower_bound(b,b+ans+1,a[i])-b;//upper_bound for Longest Non Descending Sub Sequence; if (k>ans) b[++ans]=a[i]; else if (b[k]>a[i]) b[k]=a[i]; } cout<<ans<<endl; } return 0; }*/
最长上升子串