Chain

本文针对词链问题,提出一种基于动态规划的优化算法。通过对有向无环图特性的深入分析,采用定理证明的方式逐步优化算法,最终实现时间和空间复杂度的有效降低。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

面分析Codes问题时,已经提到了这个优化思想。下面我们从有向无环图的性质入手,进行动态规划。

一、问题描述

一个词是由至少一个至多75个小写英文字母(a,b,…,z)组成的。当在一张一个或多个词组成的表(list)中,每一个词(除第一个)都能由在其前一个词的词尾添加一个或多个字母而得到的话,则称此表为一个词链。

例如:

i

in

int

integer

为一个含四个词的链,而表

input

integer

不是链.注意:所有仅含一个词的表都是链

输入:你将从文件中读到一张表. 表中至少有1个词,而所有词所含字母个数之总和不超过2000000个。文件以“.”结束。其余各行每行含一个词。各行已按字典顺序由小到大排序。文件中的词不会有重复。

输出:一个链的长度是指该链所含词的个数。你的程序应从输入文件中找出最长的链,并把该链写入文件中去。一个词占一行。若有多个最长链,只须输出其中任何一个。

输入输出举例;

下图给出了一个由7歌词的输入文件和一个对应的输出文件。输入文件中有一个长度为4的链,这里已没有比它更长的链了。

Input.Txt

Output.txt

I

If

In

Input

Integer

Output

.

i

in

int

integer

 

 

二、分析

容易看出,这道题可以使用动态规划来解决。但由于题目中的数据可能很大(2M),无论在时间上还是空间上,简单的动态规划都无法满足要求。因此,必须根据题目的特点和本题动态规划的特殊模型,对动态规划算法进行改进。

我们先看看一般的动态规划的模型:将每一个读入的单词看成一个结点。若单词i是单词j的前缀并且ij(即单词j可以由单词i的词尾添上一个或多个字母组成),则在ij之间连一条有向边,方向由ij。再在图中添加一个始点和终点,始点到任何单词有一条有向边,任何单词到终点有一条有向边,且所有边的权为1 则题目即要求从始点到终点的一条最长路径。易证上图是有向无环图。因此,求最长路径可以用动态规划。

由于输入文件已经按照字典顺序排序,我们可以先按照单词读入的次序给单词编号。即第一个单词编号为1,第二个单词编号为2,以此类推,且始点编号为0。为了便于叙述,设编号为i的单词为Wi,且记由始点到Wi的最长路径的长度为Li

这时,动态规划方程为:

初始:L0=0

方程:Li=Max{Lj+1|WjWi的前缀,j=0..单词总数}

目标:Result=Max{Li|i=1..单词总数}

算法:

  for i:=1 to n do

    for j:=1 to n do

      if WiWj的前缀and Li<Lj+1 then Li:=Lj+1

空间复杂度:n=2M=2*106

时间复杂度:n2=4*1012

为了优化方程,首先引出以下定理:

定理1:若WiWj的前缀,且iji<j

证明:根据字典顺序的定义易证(此略)。

根据定理1,原动态规划方程可改进为:Li=Max{Lj+1| WjWi的前缀,j=0..i-1}

定理2:若WiWk的前缀,WjWk的前缀,且j>i,则WiWj的前缀。

证明:根据前缀的定义易得,WiWj都是Wk尾部删去某些字符而成,j>i则说明Wj的长度比Wi的大,故WiWj的前缀。

定理3:若WiWk的前缀,且不存在jj>iWjWk的前缀,则Lk=Li+1

证明:若Lk=Lj+1,且WjWk的前缀,j<i,则由定理2WjWi的前缀。所以Li>Lj,因此,Li+1>Lj+1=Lk,这与Lk=Max{Li+1| WiWk的前缀,i=0..k-1}矛盾。

定理3说明,原动态规划方程可改进为:Li=Lj+1j为满足WjWi的最长前缀。

定理4:若i<j<k,且Wi不是Wj的前缀,则Wi不是Wk的前缀。

证明:若WiWk的前缀,根据字典顺序的定义,因为Wi<Wj<Wk,所以WiWj的前缀,这与前提Wi不是Wj的前缀相矛盾。因此,Wi不是Wk的前缀。

由定理1和定理4可得定理5Wk的前缀必定是Wk-1的前缀,或Wk-1

综合定理3和定理5,我们可以得出优化后的动态规划方程:

Wi-1Wi的前缀,则Li=Li-1+1,否则Li=Lj+1j满足WjWi-1的最长的前缀。

由于一个单词的长度最大为75个字符,因此前缀最多只可能有75个。这时,可以建立一个可能前缀集合来完成整个规划过程。具体算法如下:

begin

  可能前缀集合:={W0};

  L0=0;

  while not结束do begin

    Readln (Wi);

    j:=Select; {j满足Wj是可能前缀集合中最长的Wi的前缀}

    Li=Lj+1;

    删除可能前缀集合中所有不是Wi前缀的单词;

    在可能前缀集合中添入Wi

  end

end.

改进后的算法时空复杂度都大大的降低。但在实现的过程当中,仍然存在优化的余地。特别是对于“可能前缀集合”的数据结构的设计上。若简单的定义数组=array[1..75] of string[75];无论是添加,删除元素还是寻找最长的前缀都不方便,空间也很浪费。由定理2,“可能前缀集合”中任意两个不同的单词都存在前缀关系,任意一个非最长的单词都是最长单词的前缀。因此,我们只要保存最长的单词(即Wi),就可以通过标志分点的方法来表示“可能前缀集合”了。这时,时间复杂度降为O(N)N为文件的的大小≤106,空间复杂度降为O(75)=O(1)

由于题目要求输出所有解,因此我们可以采取两遍读文件的方法解决。第一遍找到最长词链的长度,第二遍再输出所有解。也可以使用Rewrite过程解决这个问题。具体程序见算法部分。

三、参考程序

TNode=record

  Ch: Char; L: Integer

end;

 

TSet=array[0..75] of TNode;{可能前缀集合}

 

begin

  l=0;{可能前缀集合中单词的最大长度}

  MaxL=0;{最长的词链的长度}

  Readln(Str);{读入一个单词}

  while Str’.’ do begin{没有结束}

    i:=1;

    if l>Length(Str) then l:=Length(Str);

    while (il) and (Str[i]=List[i].Ch) do Inc(i);

    x:=List[i-1].L;

    while iLength(Str) do begin{Str加入可能前缀集合中}

      List [i]. Ch: =Str [i]; List [i]. L: =x; Inc (i)

    end;

    l:=i-1;List[l].L:=x+1;{优化后的动态规划方程}

    if List[l].L>MaxL then begin{找到更长的词链}

      ReWrite (Output);

      输出词链

    end else if List[l].L=MaxL then 输出词链;{找到另一个解}

    Readln(Str){读入下一个单词}

  end

end.

四、总结

本题除了用动态规划外,还可以通过建树的方法分析,即用一颗多叉树来存储所有的单词,并通过标记的方法找到最长链。根据输入文件已经按照字典顺序排序的条件,建树的过程和找最长链的过程都可以优化,而且多叉树也可以压缩为一个线性表。分析后的程序和动态规划的是一样的,只是对程序的解释不同。

分析中所用到的5条定理,都是从题目的条件入手,根据字典顺序和词链的定义得出的。表面上是对题目的分析,实际上是对动态规划所建立的有向无环图的特点的分析。而所有的优化,都是根据有向无环图(即题目的数学模型)的特殊性而得到的。

根据题目的条件,挖掘出有向无环图的特殊性质,从而优化算法。最终,时间复杂度降为n,空间复杂度降为常数。这说明,动态规划相对于搜索来说,的确对问题的数学模型有了更精确的刻画,但并不代表不能再优化。对于很多经典的动态规划模型,特别是用有向无环图的最长路径描述的问题,有时仍然十分粗糙。这时就必须进一步的挖掘出模型的特殊性质,优化动态规划方程,从而达到优化算法的目的。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值