时间的词分解


12

给定两个字符串S1,S2,我们为它们的串联写S1S2。如果给定字符串S和整数k1,我们写(S)k=SSS为串接k的副本S。现在给定一个字符串,我们可以使用此符号“压缩”它,即AABAAB可以写为((A)2B)2。让我们称之为a的重量压缩出现在它的字符数,所以的重量((A)2B2)是两个,并且重量(AB)2A(一压缩ABABA)是三个(单独的A s分别计算)。

现在考虑用|计算给定字符串S的“最轻”压缩的问题。S | = n。经过一番思考,有一种明显的动态编程方法可以根据确切的方法在O n 3 log n O n 3)中运行|S|=nO(n3logn)O(n3)

但是,有人告诉我这个问题可以在O(n2logn)时间内解决,尽管我找不到有关如何执行此操作的任何资料。具体来说,这个问题是在最近的编程竞赛中给出的(问题K 在这里,最后两页)。在分析过程中,提出了O(n3logn)算法,最后提到了伪二次边界(此处为4分钟标记)。遗憾的是,主持人只提到了“一个复杂的单词组合引理”,所以现在我来这里寻求解决方案:-)


SS=Xa=Yb Z gcd | X || Y |X Y S = X a S Y S = Y b Y X | X |S=Z|S|/gcd(|X|,|Y|)Zgcd(|X|,|Y|)XYS=XaSYS=Yb,那么您只需尝试使用长度为的前缀。YX|X|
j_random_hacker

问题是,即使减少了所有可能的,您仍然需要通过三次DP在子段上聚合答案(即),因此在那之后还有很多工作要做…… D P [ l r ] = 最小k D P [ l k ] + D P [ k + 1 r ]XaDP[l,r]=minkDP[l,k]+DP[k+1,r]
Timon Knigge

我明白你的意思了。我认为您需要某种主导关系,以消除需要测试的一些值-但我还没有想到。特别地,我考虑了以下问题:假设具有最优分解因子其中;是否有一个最佳解决方案,其中被分解为且?不幸的是,答案是肯定的:对于,具有最优分解因子,但是的唯一最优分解因子是。S [ 1 .. i ] S [ 1 .. i ] = X Y k k > 1 S X Y j Z j < k S = A B A B C A B C S [ 1..4 ] A B 2 S A B A B C 2kS[1..i]S[1..i]=XYkk>1SXYjZj<kS=ABABCABCS[1..4](AB)2SAB(ABC)2
j_random_hacker

Answers:


1

如果我不误解您,我认为可以在时间内按如下方式计算最小成本因式分解。O(n2)

对于每个索引i,我们将为计算一堆值,如下所示。令为最小整数,使得存在满足的整数对于这个特定的,令是具有此属性的最大。如果不存在这样的,则将设置为该索引有零个值。(pi,ri)=1,2,pi11r2

S[irpi1+1,ipi1]=S[i(r1)pi1+1,i].
pi1ri1rpiLi=0(pi,ri)

令是严格大于的最小整数,同样, for a。像以前一样,将设为具有固定的最大值。通常,是严格小于的最小数字。如果不存在这样的,则。pi2(ri11)pi1

S[iri2pi2+1,ipi2]=S[i(ri21)pi2+1,i]
ri22ri2pi2pi(ri11)pi1piLi=1

请注意,由于值随几何增加,因此对于每个索引i,我们都有。(如果存在,则它不仅严格大于而且至少大于(建立了几何增量。 )Li=O(log(i+1))pipi+1(ri1)pipi/2

现在假设所有值都给了我们。最低成本由递归 的理解是,对于我们设置。该表可以用时间。(pi,ri)

dp(i,j)=min{dp(i,j1)+1,min(dp(i,jrjpj)+dp(jrjpj+1,jpj))}
i>jdp(i,j)=+O(n2+njLj)

在上面我们已经观察到通过对项和项进行限制。但是实际上,如果我们看一看全部金额,我们可以证明更清晰一些。jLj=O(jlog(j+1))=Θ(nlogn)

考虑后缀树的反向的(即,S的前缀树)。我们会和每一贡献充电到的边缘,这样每个边将最多一次充电。将每个充电到并向发出的边缘。这里的是与相对应的前缀树的叶子,nca表示最近的公共祖先。T(S)SiLiT(S)pijnca(v(i),v(ipij))v(ipij)v(i)S[1..i]

这表明。值可以通过对后缀树的遍历在时间进行计算,但是如果有人感兴趣,我将把细节留给以后的编辑。O(iLi)=O(n)(pij,rij)O(n+iLi)

让我知道这是否有意义。


-1

您的初始字符串S的长度为n。这是该方法的伪代码。

next_end_bracket = n
for i in [0:n]: # main loop

    break if i >= length(S) # due to compression
    w = (next_end_bracket - i)# width to analyse

    for j in [w/2:0:-1]: # period loop, look for largest period first
        for r in [1:n]: # number of repetition loop
            if i+j*(r+1) > w:
                break r loop

            for k in [0:j-i]:
                # compare term to term and break at first difference
                if S[i+k] != S[i+r*j+k]:
                    break r loop

        if r > 1:
            # compress
            replace S[i:i+j*(r+1)] with ( S[i:i+j] )^r
            # don't forget to record end bracket...
            # and reduce w for the i-run, carrying on the j-loop for eventual smaller periods. 
            w = j-i

我故意在“端括号”中提供了一些细节,因为它需要大量的步骤来进行堆叠和卸载,这会使核心方法不清楚。这个想法是测试第一个内部最终的收缩。例如ABCBCABCBC =>(ABCBC)²=>(A(BC)²)²。

因此,重点是要先寻找大时期。注意,S [i]是S跳过任何“(”,“)”或幂的第i个项。

  • i环为O(n)
  • j循环为O(n)
  • r + k循环在第一个差处停止时为O(log(n))

这是全局O(n²log(n))。


我不清楚r和k循环是O(log n)-甚至是分开的。什么确保在最多O(log n)次迭代后找到差异?
j_random_hacker

我是否正确理解您正在贪婪地压缩?因为那是不正确的,所以考虑例如ABABCCCABCCC,您应该将其分解为AB(ABC ^ 3)^ 2。
Timon Knigge '18

是的,您对此完全正确,我必须考虑一下。
Optidad '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.