复制字符串n次所需的最小操作数是多少?

时间:2012-05-01 03:46:13

标签: algorithm

最近,我不得不复制指定次数的字符串。假设我必须有5个字符串“bacon”的副本,并且我的编辑器中有1行“bacon”。所以我会从这开始:

bacon

以此结束:

bacon
bacon
bacon
bacon
bacon

我们还定义了三个“原子”操作:复制,粘贴和删除。 “复制”允许您复制任意行数,“粘贴”粘贴最近复制的内容,“删除”删除一行。所以,如果我有3行“bacon”:

bacon
bacon
bacon

我想要10行“bacon,”我可以这样做:

copy 2  | lines of bacon: 3
paste 2 | lines of bacon: 5
copy 5  | lines of bacon: 5
paste 5 | lines of bacon: 10

但是 i 行和 j 所需的行所需的“原子”操作的最小数量是什么,其中 i j ?是否有适用于此问题的算法技术?还是我忽略了一些明显的东西?

3 个答案:

答案 0 :(得分:3)

考虑序列删除,然后粘贴。请注意,这会产生与粘贴后删除相同的状态。所以这些操作可以来回交换。

接下来考虑序列删除,然后是“复制N”。请注意,这与“复制N”后跟删除的状态相同,因此可以交换这些序列(但只能在一个方向上)。

因此,在任何操作序列中,我们都可以将任何删除与后续操作交换,而不会更改最终结果。因此,我们可以将所有删除操作移至最终而不更改结果。

由此可见,如果任何删除出现在最佳序列中,它们可以移动到序列的末尾:

XXXXXXXXDDDD

(其中X是C或P)

但是没有后续粘贴的副本不是最佳选择,因此序列实际上如下:

XXXXXXPDDDD

现在,观察任何粘贴+删除(PD)序列都可以用复制+粘贴(CP)序列替换,其中复制只抓取少一行。这会将两个操作替换为另外两个操作,因此它不会丢失任何操作。因此,我们可以将最佳序列转换为:

XXXXXXCPDDD

我们可以再次做三次以消除所有删除:

XXXXXXCCCCP

当然,一系列副本是次优的,所以我们的答案必须如下:

XXXXXXCP

换句话说,删除绝不是最佳序列的一部分。

从不需要连续四个粘贴,因为它们总是可以通过复制+粘贴+复制+粘贴替换。 (但是,可能需要连续三个粘贴;例如,从4到13)

这就是我现在所能得到的。在这一点上,我可能只是做一个Dijkstra式广度优先搜索最短的路径......

[更新]

正如马克·彼得斯在评论中指出的那样,也不需要连续三个贴纸。证明:

复制(N)要求N小于行数。所以复制(N)+粘贴+粘贴+粘贴总是可以用复制(N)+粘贴+复制(2N)+粘贴替换。

因此,可以形成最佳序列,没有删除,连续两个副本,并且连续不超过两个粘贴。嗯,这是一个开始。

答案 1 :(得分:2)

两个操作,复制+粘贴,最多可以使行数加倍。操作与大小的比率为2:2。如果连续两次执行,则4次操作可获得4次。

三个操作,复制+粘贴+粘贴,最多可以使行数增加三倍。操作与线的比例为3:3,或与之前相同。但如果你连续两次做到这一点,你可以获得6次操作的9倍,这是一个改进。

四个操作,复制+粘贴+粘贴+粘贴,可以使行数翻两番。同样,比例在4:4相同。如果连续两次执行,则8次操作可获得16次。

五个操作,复制+ 4 *粘贴,可以将行数乘以5.但是,对于相同数量的操作,组合两个和三个操作序列可以将行数乘以6。如果你推断出这些,你可以看到没有办法击败2,3和4操作序列的组合。

我的建议是使用4个操作序列,直到结果在目标的4倍范围内,然后切换到3或2操作序列才能完成。

答案 2 :(得分:2)

让我们规范化我们正在寻找的序列。这开始就像Nemo的回答一样。

  1. 有效序列由D *(C [DP] *)*匹配。重写D C(n)→C(n)D和D P(n)→P(n)D。

  2. 有效序列与(CP *)* D *匹配。重写C(n)D→D和P(n)D→C(n-1)P(n-1)。新副本没问题,因为作为时间函数的缓冲区长度是单峰的。

  3. 有效序列由(CP *)* | D *。

  4. 匹配

    由于i≤j,我们寻找的序列匹配(CP *)*。

    1. 有效序列与(CP *)*匹配。重写C(n)P(n)P(n)P(n)P(n)P(n)→C(n)P(n)C(2n)P(2n)P(2n)C(n)

    2. 有效序列与(CP {0,4})*匹配。重写C(n)C(n')→C(n')。

    3. 有效序列与(CP {1,4})*匹配。重写C(n)P(n)P(n)P(n)P(n)→C(n)P(n)P(n)C(2n)P(2n)。我们不需要恢复副本,因为下一个命令(如果存在)是副本。

    4. 有效序列与(CP {1,3})*匹配。重写C(n)P(n)P(n)P(n)→C(n)P(n)C(2n)P(2n)。我们不需要恢复副本。

    5. 我们正在寻找的序列匹配(CP {1,2})*。缩写2(n)= C(n)P(n)和3(n)= C(n)P(n)P(n)。如果复制整个缓冲区则调用副本整个,如果不复制则调用部分。如果复制除了一行之外的整个缓冲区,则复制几乎整个。我将使用w和p和a来注释命令。

      让我们努力减少部分副本的数量。假设我们有2p(n)... 2?(n')。我们可以重写为2w(n +δ)... 2p(n'-δ)或2w(n + n')...,减少部分拷贝的数量。我们可以对3p(n)... 3?(n')做同样的事情。假设最优,我们可以做3p(n)... 2?(n')。我们不能陷入3p(n +δ)... 2p(1),因为3?(n +δ+ 1)... D相等且更短。我们可以做2p(n)... 3?(n'),但2可能最后几乎完全。

      如果我们总是使用配合处理最左边的部分副本,则此重写会收敛。进一步改写后2w(n)3w(2n)→3w(n)2w(3n)和2w(n)2w(2n)2w(4n)2w(8n)→2w(n)3w(2n)3p(5n) ,有效序列匹配2w {0,3} 3w *(| 2p | 2a?3w * 3p)。

      log(n)-time算法

      显然,最佳解决方案是长度为O(log(n))。我们可以计算每个解决方案中与上述正则表达式匹配的3w命令数的上限和上限;这些界限因均匀常数而不同。尝试所有合理的短序列命令,其中有O(log(n))。最多一份副本未指定;如果模式有效,它很容易计算它应该是什么。