来源:刘汝佳《算法竞赛入门经典--训练指南》 P60 问题6:
问题描述:给定n个整数a1,a2,...,an,按从左到右的顺序选出尽量多的整数,组成一个上升子序列(子序列可以理解为:删除0个或多个数,其他的数顺序不变)。比如,从序列1,6,2,3,7,5中,可以选上升子序列1,2,3,5,也可以选出1,6,7;但前者更长。选出的相邻元素不能相等。
O(n^2)的时间复杂度思路分析:设d[i]为以i结尾的最长上升子序列的长度,则d[i]=Max{0,d[j](满足j<i,aj<a[i])}+1。最终的答案为Max{d[i]}。
(若LIS中的相邻元素可以相等,把“<”改为“<=”即可)。
O(nlog(n))的时间复杂度思路分析:假设经过计算出的两个状态i和j满足ai<aj && d[i]==d[j];则只需要保存ai状态即可(对于后续状态k来说,若ak>ai,则ak一定大于aj,反之则不一定)。我们可以用g[i]表示当前状态(从第一个数开始)下子序列长度为i的最小数,则每考虑后续的数加入的时候,更新g[]即可(二分查找)。(Tips:该思路只适合求最大长度问题)
例题来源:http://acm.nyist.net/JudgeOnline/problem.php?pid=17
例题:nyoj 17
3 aaa ababc abklmncdefg
1 3 7
O(n^2)时间复杂度代码:
1 #include "stdio.h" 2 #include "string.h" 3 4 #define N 10100 5 6 int d[N]; 7 char str[N]; 8 9 int inline Max(int a,int b) { return a>b?a:b; } 10 11 int main() 12 { 13 int T; 14 int i,j; 15 int len; 16 scanf("%d",&T); 17 while(T--) 18 { 19 scanf("%s",str); 20 len = strlen(str); 21 int ans = 0; 22 for(i=0; i<len; ++i) 23 { 24 d[i] = 1; 25 for(j=0; j<i; j++) 26 { 27 if(str[i]>str[j]) 28 d[i] = Max(d[i],d[j]+1); 29 } 30 ans = ans>d[i]?ans:d[i]; 31 } 32 printf("%d\n",ans); 33 } 34 return 0; 35 }
O(nlog(n))的时间复杂度代码:
1 #include "stdio.h" 2 #include "string.h" 3 #define N 10005 4 #define INF 0x3fffffff 5 6 int num; //num记录当前序列中,最长子序列的长度 7 int g[N]; //g[i]保存当前序列中,长度为i的上升子序列的最小字符 8 char str[N]; 9 10 int er_fen(int l,int r,int k) //二分查找,返回值为数组g[]中小于k的最右边的数的下标 11 { 12 int mid; 13 if(k>g[r]) return r; //都比k小,返回r 14 if(k<=g[l]) return 0; //都比k大,返回0 15 while(l+1!=r) 16 { 17 mid = (l+r)/2; 18 if(g[mid] < k) 19 l = mid; 20 else 21 r = mid; 22 } 23 return l; 24 } 25 26 int main() 27 { 28 int T; 29 int i,k,len; 30 scanf("%d",&T); 31 getchar(); 32 while(T--) 33 { 34 scanf("%s",str); 35 len = strlen(str); 36 for(i=0,num=0; i<len; i++) 37 g[i] = INF; 38 num = 1; 39 g[1] = str[0]; 40 for(i=1; i<len; i++) 41 { 42 k = er_fen(1,num,(int)str[i]); 43 if(g[k+1] > str[i]) 44 g[k+1] = str[i]; 45 if(k==num) 46 num++; 47 } 48 printf("%d\n",num); 49 50 } 51 return 0; 52 }
;