2014-10-04 81 views
1

字符串假设你有一个函数quality(x)返回字母x序列的质量。给定一个字符串,如“howareyoutoday,”什么是最有效的方式来确定分割是“你今天怎么”(即品质(如何)+质量(是)+质量(你)+质量(今天)是最高质量可能)?分割使用动态规划

我在想,我们可以有类似如下:

A[0] = h, A[1] = o, ..., A[n] = y 
Q[0] = quality(A[0]), Q[1] = quality(A[0]A[1]), ..., Q[n] = quality(A[0]...A[n]) 

我们确定的分割,我们发现最大{Q [0],...,Q [N]},它会返回一些Q [我](第一个空间在此之后)。然后,我们找到返回另一个Q [i]的最大{Q [i + 1],... Q [n]}(等于此之后的第二个空间)等,直到最大返回Q [n]。

我有两个问题:这是否方法使用动态编程?在我看来,它确实如此,因为我们用最初的问题的子问题构建了初始Q值。此外,这是一个最佳的解决方案?据我了解,最坏的情况将是为O(n^2),最大时返回Q [0],那么Q [1],然后Q [2]等

+0

此问题可能更适合[理论上的CS Stack Exchange](http://cstheory.stackexchange.com/)和[CS Stack Exchange](http://cs.stackexchange.com/) – 2014-10-04 03:49:02

+0

有一个我想这里的问题很少。首先,找到第一个单词后(假设它是“如何”),你说你想找到最大{Q [i + 1],..,Q [n]}(我假设你的意思是i + 1 ,而不是i + i) - 但这意味着质量()将在整个字符串上被调用,即质量(“howa”),质量(“howar”),质量(“howare”)等。 – 2014-10-04 03:57:04

+0

Second ,这似乎是一种贪婪的方法,不一定会给你提供最佳的解决方案,因为你决定在哪里做第一次休息,而不考虑这可能会如何影响下游决策。例如。对于字符串“theredcar”,你会希望第一个单词是“the”,但对于字符串“thereitgoes”它应该是“there”。如果你给“有”低于“该”的质量分数,那么你的算法在第二个输入中总是错误的,而如果你给它一个更高质量的分数,它总是在第一个上是错误的 - 你必须*看看字符串的其余部分来决定。 – 2014-10-04 04:03:46

回答

1

您可以使用动态编程这将是如果你可以使用缓存的子问题结果。例如,如果质量(A [0] A [1])是质量(A [0])和质量(A [1])的函数(例如质量(A [0] A [1])=质量A [0])+ quality(A [1])),那么您可以通过缓存子问题的结果来获益;如果质量(A [0] A [1])与质量(A [0])和质量(A [1])无关,则缓存子问题的结果不会使您受益,使用动态编程。

您的算法的最坏情况是O(n^2)。您可能能够使用潜在的次优O(n)解决方案:测试质量(A [0]),质量(A [0] A [1]),质量(A [0] A [ 1] A [2])等等;如果质量(A [0] A [1])>质量(A [0] A [1] A [2])在当前解决方案的质量低于先前解决方案的质量时停止, A [0] A [1]并开始搜索A [2]中的下一个单词。您可以通过增加前瞻来以更大的常数因子为代价来提高算法的质量;如果质量(A [0] A [1])>质量(A [0] A [1]),则刚刚描述的算法具有前瞻性(1),而先行(2)算法将在A [0] 1] A [2])和质量(A [0] A [1])>质量(A [0] A [1] A [2] A [3])。制定这是一个DP问题

1

的一种方法是计算一个函数f(I):

f(i) = The highest sum of quality scores achievable on the first i characters. 

我们注意的是,最后一段必须在定位端的i-1,它可以开始在它之前的任何位置,回到位置0(假设我们从0开始编号)。我们称这段j的起始位置为;我们需要尝试j的所有可能值,并找到最好的值。我们应该把这个最后的部分A [j] .. A [i-1]与?使用最佳的解决方案,无论剩余在其左边,这通常由f(j)给出。所以我们得到:

f(i) = max { f(j) + quality(A[j]..A[i-1]) } over all 0 <= j <= i. 

其中f(0)= 0作为边界情况。

F(N + 1)将计算在二次时间和使用线性内存要求整个字符串的最佳得分。要真正计算出单词中断的位置应该得到这个分数,您需要从f(n + 1)向后走,确定哪个j值使其达到最大值(可能有几个;在这种情况下, )。继续重复这个操作直到你得到j = 0(或者你可以在计算f()时将这些j的“最大化”值存储在一个单独的数组中,这个数组通常被称为“pred []”。)

[编辑]

上面是解决该问题的递归。它会正确解决,但非常缓慢的大n。所有DP算法都可以用这种方式表述,但要将它从简单的递归转换为DP算法,您需要输入memoize它,或者找出如何从中自下而上填充表格填充迭代算法。递归的记忆是微不足道的:只要函数计算出我的新值的答案,就存储这个答案(这里是一个大小为n + 1的数组),下次使用它时要求f(i)的特定值。自下而上的DP涉及计算可填充表的顺序,以便满足所有依赖关系。这可以是(恒定的因素)更快,但有时难以做到。

+0

非常简洁。感谢您的帮助!如果没有太多的麻烦,你可以提供一个算法的例子吗? – 2014-10-05 01:05:13

+0

@BobJonas:对不起,但我认为需要大量的时间和空间才能给出一个有用的例子,并且它不会向你展示任何东西,比如果你实现了递归(只有几行)并且在调试器。然而,最好的事情是让自己相信:(a)分解由前i个字符组成的前缀的每种有效方式都有一个最右边的段落在i-1位置结束,并且(b)在任何位置j这个最右边的片段开始,用这个片段A [j] ... A [i-1]给出的解决方案的最佳总分由f(j)+ quality(A [j] ... A [i-1])给出'。 – 2014-10-05 01:25:14

+0

好的,这很有道理。还有一件事:为什么我们计算两个值之间的最小值而不是最大值? – 2014-10-05 23:04:53