O(n2)比较慢
#define MAX(x,y) (x>y?x:y)
int findNumberOfLIS(int* nums, int numsSize){
int len[numsSize];//存储以nums[i]为结尾的最长子序列的长度
int cnt[numsSize];//存储以nums[i]为结尾的最长子序列的个数
int ans[numsSize+1];//存储长度为i的最长子序列的个数
memset(len,0,sizeof(len));
memset(cnt,0,sizeof(cnt));
memset(ans,0,sizeof(ans));
len[0]=1;
cnt[0]=1;
ans[1]=1;
int maxx_lenth;
int maxx=0;
for(int i=1;i<numsSize;i++)
{
maxx_lenth=0;
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])
{
maxx_lenth=MAX(maxx_lenth,len[j]);
}
}
len[i]=maxx_lenth+1;
int tempsum=0;
for(int j=0;j<i;j++)
{
if(len[j]==maxx_lenth&&nums[j]<nums[i])
{
tempsum+=cnt[j];
}
}
if(tempsum!=0)
cnt[i]=tempsum;
else
cnt[i]=1;
maxx=MAX(maxx,len[i]);
ans[len[i]]+=cnt[i];
}
maxx=MAX(maxx,len[numsSize-1]);
return ans[maxx];
}
优化后比较快
优化的思想是,在这个程序中涉及到求cnt[i]和最后的答案
前者要进行的操作是在小于i的所有数j中选择最大的len[j],求sigma(cnt[j]),len[j]==max
后者要进行的操作是在所有i中选择最大的len[j],求sigma(cnt[i]),len[i]==max
都涉及到求值为最大值的个数这一操作
所以可以维护最大值,然后如果当前值比最大值大,则重置cnt为当前值的cnt,并更新最大值
如果当前值和最大值一样则ans_cnt+=cnt[当前值]
int findNumberOfLIS(int* nums, int numsSize){
int len[numsSize];//存储以nums[i]为结尾的最长子序列的长度
int cnt[numsSize];//存储以nums[i]为结尾的最长子序列的个数
len[0]=1;
cnt[0]=1;
int maxlen=1;
int ans=1;
for(int i=1;i<numsSize;i++)
{
len[i]=1;
cnt[i]=1;
for(int j=0;j<i;j++)
{
if(nums[j]<nums[i])
{
if(len[j]+1>len[i])
{
len[i]=len[j]+1;
cnt[i]=cnt[j];
}
else if(len[j]+1==len[i])
{
cnt[i]+=cnt[j];
}
}
}
if(len[i]>maxlen)
{
maxlen=len[i];
ans=cnt[i];
}
else if(len[i]==maxlen)
{
ans+=cnt[i];
}
}
return ans;
}
但还可以更快
首先先考虑只求一个序列的最长递增子序列的长度
官方题解
int lengthOfLIS(int* nums, int numsSize){
int d[numsSize+1]; //长度为i<->d[i];
d[1]=nums[0];
int len=1;
for(int i=1;i<numsSize;i++)
{
if(nums[i]>d[len])
{
len++;
d[len]=nums[i];
}
else
{
//二分查找第一个比nums[i]小的
int left=1,right=len,mid;
while(left<=right)
{
mid=(left+right)>>1;
if(d[mid]<nums[i])
{
left=mid+1;
}
else
right=mid-1;
}
d[right+1]=nums[i];
}
}
return len;
}
首先明确d[i][j]这个二维的数组,每次最后加入的数是单调递增的
整个算法的过程是遍历nums数组
对于每个数寻找他的插入位置,因为每个数的插入位置前面的数都要比他小,比如d[len][j]<当前要插入的数,插入后,len变成了len+1
第一步根据最后一步加入的数也就是1 4 6 7 寻找要插入的一维数组的位置,(第一个比他大的数)比如插入4的时候,他可以插在1后面,这时候长度变成了2,也就是d[2]这个数组
第二部在d[i-1]数组内部寻找准确插入的位置,在一维数组的内部是递减的,因为如果有个数比前面的数大,那他就可以插在后一个长度的位置上了。
就找第一个比他小的位置k,从k到最后的数都满足
所以cnt[i][当前位置的数]=cnt[i-1][k]+……+cnt[i-1][最后的数]
这一步又可以用前缀和进行优化,变成cnt[i-1][最后的数]-cnt[i-1][k-1]又因为cnt[i]是从0开始,cnt[i][0]=0,也就是比如k=2 照理来说应该是cnt[i-1][最后的数]-cnt[1][k-1],但是cnt[i]是这样的0,cnt[i][第一个数],cnt[i][第二个数]所以还是cnt[i-1][最后的数]-cnt[i-1][k],找到在len=i-1的插入位置后,再把当前的数pushback到d[len]里
仅作记录
class Solution {
int binarySearch(int n, function<bool(int)> f) {
int l = 0, r = n;
while (l < r) {
int mid = (l + r) / 2;
if (f(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
return l;
}
public:
int findNumberOfLIS(vector<int> &nums) {
vector<vector<int>> d, cnt;
for (int v : nums) {
int i = binarySearch(d.size(), [&](int i) { return d[i].back() >= v; });
int c = 1;
if (i > 0) {
int k = binarySearch(d[i - 1].size(), [&](int k) { return d[i - 1][k] < v; });
c = cnt[i - 1].back() - cnt[i - 1][k];
}
if (i == d.size()) {
d.push_back({v});
cnt.push_back({0, c});
} else {
d[i].push_back(v);
cnt[i].push_back(cnt[i].back() + c);
}
}
return cnt.back().back();
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/number-of-longest-increasing-subsequence/solution/zui-chang-di-zeng-zi-xu-lie-de-ge-shu-by-w12f/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。