我试图解决顶级编码器上的问题zig zag序列。我的代码的时间复杂度是O(n * n)。如何将其减少为O(n)或O(nlog(n)) 伪代码或算法的解释对我很有帮助 这是问题陈述。 问题陈述
如果连续数字之间的差异在正数和负数之间严格交替,则数字序列称为Z字形序列。第一个差异(如果存在)可以是正面的也可以是负面的。少于两个元素的序列通常是一个Z字形序列。
例如,1,7,4,9,2,5是Z字形序列,因为差异(6,-3,5,-7,3)交替为正和负。相比之下,1,4,7,2,5和1,7,4,5,5不是Z字形序列,第一个是因为它的前两个差异是正的,第二个是因为它的最后差异是零。
给定一系列整数序列,返回序列的最长子序列的长度,该序列是Z字形序列。通过从原始序列中删除一些元素(可能为零)来获得子序列,将剩余的元素保留为原始顺序。
这是我的代码
#include <iostream>
#include<vector>
#include<cstring>
#include<cstdio>
using namespace std;
class ZigZag
{
public:
int dp[200][2];
void print(int n)
{
for(int i=0;i<n;i++)
{
cout<<dp[i][0]<<endl;
}
}
int longestZigZag(vector<int> a)
{
int n=a.size();
//int dp[n][2];
for(int i=0;i<n;i++)
{
cout<<a[i]<<" "<<"\t";
}
cout<<endl;
memset(dp,sizeof(dp),0);
dp[0][1]=dp[0][0]=1;
for(int i=1;i<n;i++)
{
dp[i][1]=dp[i][0]=1;
for(int j=0;j<i;j++)
{
if(a[i]<a[j])
{
dp[i][0]=max(dp[j][1]+1,dp[i][0]);
}
if(a[j]<a[i])
{
dp[i][1]=max(dp[j][0]+1,dp[i][1]);
}
}
cout<<dp[i][1]<<"\t"<<dp[i][0]<<" "<<i<<endl;
//print(n);
}
cout<<dp[n-1][0]<<endl;
return max(dp[n-1][0],dp[n-1][1]);
}
};
答案 0 :(得分:5)
U可以使用贪婪方法在 O(n)中执行此操作。取第一个不重复的数字 - 这是你的之字形子序列的第一个数字。检查阵列中的下一个数字是否小于或大于第一个数字。
案例1:如果较小,请检查下一个元素并继续运行,直到找到最小元素(即)之后元素大于前一个元素。这将是你的第二个元素。
案例2:如果更大,请检查下一个元素并继续前进,直到找到最大元素(即)之后的元素将小于前一个元素。这将是你的第二个元素。
如果您使用案例1查找第二个元素,请使用案例2查找第三个元素,反之亦然。在这两种情况之间保持交替,直到原始序列中没有更多元素。得到的数字将形成最长的之字形子序列。
例如:{1,17,5,10,13,15,10,5,16,8}
结果子序列:
1 - &gt; 1,17(案例2) - &gt; 1,17,5(案例1) - &gt; 1,17,5,15(案例2) - &gt; 1,17,5,15,5(案例1) - &gt; 1,17,5,15,5,16(案例2) - &gt; 1,17,5,15,5,16,8(案例1)
因此,最长的之字形子序列的长度为7。
你可以参考sjelkjd的solution来实现这个想法。
答案 1 :(得分:1)
由于子序列不一定是连续的,你不能使它成为O(n)。在最坏的情况下,复杂度是O(2 ^ n)。但是,我做了一些检查以尽快切断子树。
int maxLenght;
void test(vector<int>& a, int sign, int last, int pos, int currentLenght) {
if (maxLenght < currentLenght) maxLenght = currentLenght;
if (pos >= a.size() || pos >= a.size() + currentLenght - maxLenght) return;
if (last != a[pos] && (last - a[pos] >= 0) != sign)
test(a,!sign,a[pos],pos+1,currentLenght+1);
test(a,sign,last,pos+1,currentLenght);
}
int longestZigZag(vector<int>& a) {
maxLenght = 0;
test(a,0,a[0],1,1);
test(a,!0,a[0],1,1);
return maxLenght;
}
答案 2 :(得分:0)
您可以使用RMQs删除内部for循环。当您找到dp[i][0]
和dp[i][1]
的答案时,请将其保存在两个RMQ树中 - 例如,RMQ 0 和RMQ 1 - 就像您一样现在正在使用dp
数组的两行。因此,当您计算dp[i][0]
时,将值dp[i][0]
放在RMQ 0 中的位置a[i]
上,这意味着存在长度为{的Z字形序列{ {1}}越来越多地以数字dp[i][0]
结束。
然后,为了计算a[i]
,您不必遍历0和dp[i + 1][0]
之间的所有数字。相反,您可以查询RMQ 0 以获取位置上的最大数字&gt; i
。这将为您提供最长的Z字形子序列,其末尾的数字大于当前的数字 - 即可以用数字a[i + 1]
递减的最长的一个。然后你可以为RMQ 1 做另外一半的zig-zag子序列。
由于您可以实现查询复杂度为a[i + 1]
的动态RMQ,因此整体复杂度为O(log N)
。
答案 3 :(得分:0)
您可以在O(n)
时间和O(n)
额外空间中解决此问题。
算法如下。
n-1
return (result+1)
这是它在C ++中的实现
#include <iostream>
#include <vector>
using namespace std;
int main()
{
ios_base::sync_with_stdio(false);
int n;
cin>>n;
vector<int> data(n);
for(int i = 0; i < n; i++)
cin>>data[i];
vector<int> diff(n-1);
for(int i = 1; i < n; i++)
diff[i-1] = data[i]-data[i-1];
int res = 1;
if( n < 2)
cout<<res<<"\n";
else
{
int temp_idx = 0;
for(int i = 1; i < n-1; i++)
{
if(diff[i]*diff[i-1] < 0)
{
temp_idx++;
res++;
}
else
{
res = max(res,temp_idx);
temp_idx = 1;
}
}
cout<<res+1<<"\n";
}
return 0;
}
答案 4 :(得分:0)
这是一个纯粹的理论解决方案。这就是你如何解决它,如果你在一个学术环境中被要求它,站在黑板旁边。
可以使用动态编程创建问题的解决方案:
子问题具有以下形式:如果我有序列的元素x,那么以该元素结尾的最长子序列是什么?
然后你可以使用递归调用来解决你的解决方案,它应该看起来像这样(关系的方向可能是错误的,我还没有检查过):
S - given sequence (array of integers)
P(i), Q(i) - length of the longest zigzag subsequence on elements S[0 -> i] inclusive (the longest sequence that is correct, where S[i] is the last element)
P(i) = {if i == 0 then 1
{max(Q(j) if A[i] < A[j] for every 0 <= j < i)
Q(i) = {if i == 0 then 0 #yields 0 because we are pedantic about "is zig the first relation, or is it zag?". If we aren't, then this can be a 1.
{max(P(j) if A[i] > A[j] for every 0 <= j < i)
这应该是O(n)
并且具有正确的记忆(存储Q(i)和P(i)的每个输出),因为每个子问题仅计算一次:n*|P| + n*|Q|
。
这些调用返回解决方案的长度 - 只要找到最大值,就可以通过存储“父指针”来找到实际结果,然后在这些指针上向后遍历。
您可以通过使用数组查找替换函数调用来避免递归:P[i]
和Q[i]
,并使用for
循环。