正如已经指出的那样,该问题类似于更常见的编辑距离问题(位于Levenshtein距离的下方)。它还具有例如动态时间规整距离(在您的最后一个需求中重复或“停顿”)的共性。
迈向动态编程的步骤
x=x1…xny=y1…ymd(x,y)
min⎧⎩⎨⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪⎪d(x,y1…ym−1)+1d(x,y2…ym)+1d(x,y1…ym/2)+1d(x1…xn/2,y)+1d(x1…xn,y)+1d(x1…xn−1,y1…ym−1)if y=y1…ym/2y1…ym/2if x=x1…xn/2x1…xn/2if yn=ym▻ Add letter at end▻ Add letter at beginning▻ Doubling▻ Halving▻ Deletion▻ Ignoring last elt.
在这里,最后一个选项基本上说将FOOX转换为BARX等效于将FOO转换为BAR。这意味着您可以使用“末尾添加字母”选项来实现口吃(重复)效果和一点删除。问题是,它会自动让你添加一个任意的字符串中间性格,以及,你可能不想要的东西。(这种“忽略相同的最后一个元素”是在任意位置实现删除和口吃的标准方法。它的确禁止了任意插入,同时允许在任一端进行添加,但是有些棘手……)
我已经包括了此故障,即使它不能完全完成工作,以防万一其他人可以某种方式“营救”该故障,也因为我在下面的启发式解决方案中使用了它。
(当然,如果您得到这样的细分,实际上定义了您的距离,则只需添加备注即可,并且您将找到解决方案。但是,因为您不仅在处理前缀,所以我不会认为您可以只使用索引进行记忆;您可能必须为每个调用存储实际的,经过修改的字符串,如果您的字符串大小足够大,它将变得很大。)
迈向启发式解决方案
另一种方法可能更容易理解,并且可以使用更少的空间,它是使用算法(从基本上,最好是首先分支定界)。搜索空间将直接由您的编辑操作定义。现在,对于一个大字符串,您将A∗得到一个较大的邻居,因为您可以删除任何字符(为每个可能的删除操作给您一个邻居),或复制任何字符(再次为您提供线性数量的邻居),以及在任一端添加任何字符,这将给您的邻居数量等于字母大小的两倍。(只希望您不使用完整的Unicode ;-)使用如此大的扇出功能,您可以使用双向 或一些相对的A∗来实现相当大的加速。
为了使起作用,您需要为到目标的剩余距离设置一个下限。我不确定这里是否有明显的选择,但是您可以做的是根据我上面给出的递归分解实现一个动态编程解决方案(如果字符串很长,还会出现空间问题)。尽管该分解不能完全计算出您的距离,但可以保证它是一个下界(因为它更宽松),这意味着它将在用作启发式方法。(我不知道它有多紧,但这是正确的。)当然,绑定函数的备注可以在期间的所有绑定计算中共享A∗A∗A∗跑。(在那里进行时间/空间折衷。)
所以…
我提出的解决方案的效率似乎在(1)字符串的长度和(2)字母表的大小上花了很多时间。如果两者都不大,则可能有效。那是:
- 使用我的递归分解和动态编程(例如,使用记忆的递归函数)来实现距离的下限。
- 通过状态空间中的“动作”以及基于动态编程的下限,将编辑操作实现为(或双向)。A∗A∗
我不能真的保证效率如何,但是应该是正确的,并且它可能比暴力解决方案要好得多。
如果没有别的,我希望这会给您一些进一步调查的想法。