【问题标题】:Longest bitonic subsequence in O(n*logn) complexityO(n*logn) 复杂度中最长的双调子序列
【发布时间】:2016-05-06 16:15:04
【问题描述】:

如果一个子序列单调增加然后单调减少,或者如果它可以循环移动到单调增加然后单调减少,则它是双调的。

给定一个序列,如何有效地确定最长的双调子序列?

edit:将标题编辑为子序列

【问题讨论】:

  • 已删除的答案(仅链接,因此已正确删除)指向this claimed O(n) solution。有人应该随时在此处总结该页面的有趣见解。
  • 我还没有确认在 O(n) 时间内返回的方法,它们通常属于我在代码中给出的示例数组。谢谢你可爱的编辑,你真的让整个事情变得更清晰了!您提供的链接中的那个 + 我在 cmets 中检查的一些结果是 4 而不是 6。这些 O(n) 方法不能很好地处理对它们有多个峰值的数组,因为它们没有结合了 LIS 算法的基本原理,它们大多是基于贪婪的。
  • @GiladMitrani:很容易对我们提出的解决方案感到自满。该链接中给出的解决方案是正确的。也许您不理解链接中的问题。最长双声子阵列和最长双声子序列之间存在差异。对于子数组(意味着元素必须是连续的),最优解在 O(N) 中。
  • 我确实不明白有问题的区别。我已经编辑了我原来的帖子标题,以更好地适应这个新发现的知识。感谢您指出这一点。

标签: java algorithm lis lds


【解决方案1】:

这里包含了一个完整的可编译算法示例:

import java.util.Arrays;

public class LBS {

public static int binarySearchBetween(int[] arr, int end, int val) {
    int low = 0, high = end;
    if (val < arr[0]) {
        return 0;
    }
    if (val > arr[end]) {
        return end + 1;
    }
    while (low <= high) {
        int mid = (low + high) / 2;
        if (low == high) {
            return low;
        } else {
            if (arr[mid] == val) {
                return mid;
            }
            if (val < arr[mid]) {
                high = mid;
            } else {
                low = mid + 1;
            }
        }
    }
    return -1;
}

/**
 * Returns an array of LIS results such that arr[i] holds the result of the 
 * LIS calculation up to that index included.
 * @param arr The target array.
 * @return An array of LIS results.
 */
public static int[] lisArray(int[] arr) { // O(n*logn) 
    /* Regular LIS */
    int size = arr.length;
    int[] t = new int[size]; 
    t[0]=arr[0];
    int end = 0;

    /* LIS ARRAY */
    int[] lis = new int[size]; // array for LIS answers.
     // Start at 1 (longest sub array of a single element is 1)
    lis[0]=1; 

    for (int i=1; i<size; i++) { // O(n) * O(logn) 
        int index = binarySearchBetween(t, end, arr[i]);
        t[index] = arr[i];
        if (index > end) {
            end++;
        }
        lis[i]=end+1; // saves the current calculation in the relevant index
    }
    return lis;
}

/*
* Input:  {1, 11, 2, 10, 4, 5, 2, 1}
* Output: {1,  2, 2,  3, 3, 4, 4, 4}
* Each index in output contains the LIS calculation UP TO and INCLUDING that 
* index in the original array.
*/

public static int[] ldsArray(int[] arr) { // O(n*logn)
    int size = arr.length;
    int t[] = new int[size];
    for (int i = 0; i < size; i++) {
        t[i] = -arr[i];
    }
    int ans[] = lisArray(t);
    return ans;
}

public static int lbs(int[] arr) { // O(n*logn)
    int size = arr.length;
    int[] lis = lisArray(arr); // O(n*logn)
    int[] lds = ldsArray(arr); // O(n*logn)
    int max = lis[0]+lds[size-1]-1;
    for (int i=1; i<size; i++) { // O(n)
        max = Math.max(lis[i]+lds[size-i]-1, max);
    }
    return max;
}

public static void main (String[] args)
{
        int arr[] = {1,11,2,10,4,5,2,1};
        System.out.println(Arrays.toString(arr));
        System.out.println(lbs(arr));
}
}

解释:

首先,二分搜索使用 O(logn) 的复杂度,它是给定的,我不会在这个线程中解释这个。

此方法使用 LIS 的动态编程版本,其复杂度为 O(n*logn)(也是一个给定的,此处不再解释) 动态 LIS 算法返回最长子数组的长度。稍作修改,我们将长度为 n 的最长子数组保存到一个数组中,直到并包括该索引。

所以在每个索引中我们都知道“索引的最大长度” 接下来我们对LDS做同样的事情。 这将为我们提供“索引的最大长度”的值

然后我们交叉组合这些值,这给了我们“到索引的最大长度 + 从索引的最大长度”的值

现在,由于 index 中的元素被计算了两次,我们删除了一个。从而得出公式:

lbs[i] = lis[i]+lds[n-i]-1 for n>=1;

至于复杂性,以下命令:

int[] lis = lisArray(arr); // O(n*logn)
int[] lds = ldsArray(arr); // O(n*logn)

每个作品O(n*logn) 复杂度

和for循环:

for (int i=1; i<size; i++) { // O(n)
        max = Math.max(lis[i]+lds[size-i]-1, max);
    }

O(n) 复杂度下工作 所以总数是O(n*logn)+O(n*logn)+O(n) = O(n*logn)

【讨论】:

  • 你的解是最长双声子序列问题还是最长双声子阵列问题?
  • 另外,你能解释一下二分搜索是如何获取 LIS 的吗?二进制搜索用于排序数组,它的输入数组如何排序?我按照代码进行了操作,但是稍微解释一下可以帮助我更快地理解它。
  • @Aravind 我的解决方案给出了最长双声子阵列的length。动态编程 LIS 结合了二进制搜索,以减少查找最后读取元素的正确位置所需的检查量。原始数组未排序,但存储子数组的数组已排序。二进制搜索发生在该排序的辅助数组中。我希望这会有所帮助:P
  • 您的解决方案不计算最长双调子数组。也许,您正在尝试最长双调子序列问题。你能清楚地说明你解决的问题吗?
  • @Aravind 你是对的,这对我来说是一个翻译错误。已编辑。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-23
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 2023-04-01
  • 1970-01-01
相关资源
最近更新 更多