非重叠最大评分序列的最优解

时间:2009-09-11 06:52:59

标签: algorithm set

在开发模拟器的一部分时,我遇到了以下问题。考虑一个长度为N的字符串,以及该字符串的M个子字符串,并为每个字符串分配一个非负分数。特别感兴趣的是满足以下要求的子串集:

  1. 它们不重叠。
  2. 他们的总分(总和,为简单起见)是最大的。
  3. 他们跨越整个字符串。
  4. 我知道天真的暴力解决方案具有O(M * N ^ 2)复杂度。虽然这个算法的实现可能不会对整个项目的性能造成太大影响(远离关键路径,可以预先计算等),但它确实不适合我。 我想知道是否有更好的解决方案可以解决这个问题,如果有的话,它们是什么?相关代码的指针总是受到赞赏,但只是算法描述也会这样。

4 个答案:

答案 0 :(得分:2)

这可以被认为是找到通过DAG的最长路径。字符串中的每个位置都是一个节点,每个子字符串匹配都是一个边。您可以通过归纳简单地证明,对于最佳路径上的任何节点,从开始到该节点以及从该节点到结束的最佳路径的串联与最佳路径相同。多亏了你,你可以跟踪每个节点的最佳路径,并确保在开始考虑包含节点的路径之前访问了节点中的所有边缘。

然后你就会遇到问题,找到从节点开始的所有边,或者在给定位置匹配的所有子字符串。如果您已经知道子字符串匹配的位置,那么它就像构建哈希表一样简单。如果不这样做,如果使用Rabin-Karp,仍然可以构建哈希表。

请注意,通过这种方式,您仍然可以访问DAG中O(e)复杂性的所有边缘。或者换句话说,一旦从开始到结束的连接子串序列中的每个子串匹配,您将不得不考虑。通过对子字符串进行预处理以找到排除某些匹配的方法,您可以比这更好。我怀疑是否可以为此提供一般的案例复杂性改进,并且任何实际的改进在很大程度上取决于您的数据分布。

答案 1 :(得分:0)

O(N + M)溶液:

Set f[1..N]=-1
Set f[0]=0
for a = 0 to N-1
    if f[a] >= 0
        For each substring beginning at a
            Let b be the last index of the substring, and c its score
            If f[a]+c > f[b+1]
                Set f[b+1] = f[a]+c
                Set g[b+1] = [substring number]
Now f[N] contains the answer, or -1 if no set of substrings spans the string.
To get the substrings:
b = N
while b > 0
    Get substring number from g[N]
    Output substring number
    b = b - (length of substring)

答案 2 :(得分:0)

目前尚不清楚M子串是否作为输入字符串中的字符序列或indeces给出,但问题并没有因此而发生太大变化。

让我们得到长度为N的输入字符串S和M个输入字符串Tj。设Lj为Tj的长度,Pj为字符串Sj给出的得分。我们说那个字符串

这称为Dynamic Programming或DP。你保留一个长度为N的整数的数组res,其中第i个元素表示如果他只有从第i个元素开始的子字符串可以得到的分数(例如,如果输入是“abcd”,那么res [ 2]将代表你可以获得“cd”的最高分。)

然后,从末尾到开头遍历此数组,并检查是否可以从第i个字符开始字符串Sj。如果可以,那么(res [i + Lj] + Pj)的结果显然是可以实现的。迭代所有Sj,对于可以应用于第i个字符的所有Sj,res [i] = max(res [i + Lj] + Pj)。

res [0]将是你的最后一击。

答案 3 :(得分:0)

输入:

N, the number of chars in a string
e[0..N-1]: (b,c) an element of set e[a] means [a,b) is a substring with score c.  

(如果所有子串都可以,那么你可以只有c(a,b)。)

例如[1,2]我们指的是覆盖字符串第二个字母的子串(半开间隔)。

(不允许空子串;如果是,那么只有当你允许它们被“取”最多k次时才能正确处理它们)

输出:

s[i] is the score of the best substring covering of [0,i)
a[i]: [a[i],i) is the last substring used to cover [0,i); else NULL

算法 - 如果间隔e不稀疏,则为O(N ^ 2); O(N + E)其中e是允许间隔的总数。这有效地找到了通过非循环图的最佳路径:

for i = 0 to N:
    a[i] <- NULL
    s[i] <- 0
a[0] <- 0
for i = 0 to N-1
    if a[i] != NULL
        for (b,c) in e[i]:
            sib <- s[i]+c
            if sib>s[b]:
                a[b] <- i
                s[b] <- sib

产生最佳覆盖三元组(a,b,c),其中[a,b)的成本为c:

i <- N
if (a[i]==NULL):
    error "no covering"
while (a[i]!=0):
    from <- a[i]
    yield (from,i,s[i]-s[from]
    i <- from

当然,您可以将对(sib,c)存储在s [b]中并保存减法。