N个字符串的最大公共子串

求最长的公共子串(NOI’93第一题)

求N个字符串的最长公共子串,N<20,字符串长度不超过255。例如N=3,由键盘

依次输入3个字符串为

Whatislocalbus?

Namesomelocalbuses.

loca1busisahighspeedI/Obusclosetotheprocessor.

则最长公共子串为“localbus”。

此题也是作为第一题出现,同样有很多人在此题上失分。我们都做过求n个数最大公

约数的问题,在那道题中求3个数的最大公约数时,可以先求两个数的最大公约数,再将

此数与第三个数求一次最大公约数。有人从那道题中得到“启发”,设s(p,q)为字符串p

和q的最长公共子串,则p、q、r的最长公共子串为s(s(p,q),r)。这样只需编写一个求两

个字符串的最长公共子串的过程即可。但这种方法对不对呢?看看下面的例子。

三个字符串分别为‘abc’、cab’、‘c’,则s(p,q)=‘ab’,s(s(p。,q),r)=‘’。事实上这

三个字符串有公共子串‘c’。显然上面的算法是错误的,原因在于没有考虑到本题与求最

大公约数那道题在性质上的不同之处。最大公约数可以由局部解得到全局解,而本题却不

能。正确的做法是列举出其中一个字符串的所有子串,找出其中最长的而且是公共的

子串。

FORi:=1TO第一个字符串的长度DO

FORj:=iTO 第一个字符串的长度DO

IF(第i个字符到第j个字符的子串为公共子串

AND(j-i十1>当前找到的最长公共子串的长度max)

THENBEGIN

max←j-i十1;

最长公共子串←此子串;

END;

为了提高效率,我们可以将最短的字符串作为第一个字符串。此题需要考虑的并不像

多项式加法那道题那么多,但是它提醒我们在不清楚题目的性质之前,不能滥用以前的方

法。

4.可重复排列(NOI’94第一题)

键盘输入一个仅由小写字母组成的字符串,输出以该串中任取M个字母的所有排列

及排列总数(输入数据均不需判错)。

此题是由全排列问题转变而来,不同之处在于:一个字符串中可能有相同的字符,导

致可能出现重复的排列。例如从字符串‘aab’中取2个字符的排列只有三种:‘aa’、‘ab’、

‘ba’。如何去掉那些可能重复的排列呢?一种想法就是每产生一种不同的排列就记录下

来,以便让以后产生的排列进行比较判重。这种想法显然没有考虑到随着字符串长度的增

加,排列将会多得无法记录,而且这种判重方法在效率上也会很低。最好有一种方法能在

产生排列的过程中就能将重复的去掉。先看一看全排列的递归过程:

PROCEDUREWork(k);

BEGIN

IFk=m十1

THEN打印结果

ELSEFORi←1TO字符串长度nDO

IFi<>e〔1〕,e[2〕…e[k-1]

THEN

BEGIN

e〔k〕←i;

Workk十1);

END;

END;

让我们来分析产生重复的原因。考虑从字符串‘aab’中取2个字符的排列。当e〔1]从

1变到2时,字符串中的字符却没有变,都是‘a’。这样我们只要在改变e〔k]时,判断其对

应的字符是否也改变。即在上面的过程的循环中加一句判断(设字符串为s):IFs[i]<>

s[e[k]]THEN…;这当然只是一个粗略的想法,我们仅仅用上面的例子就能发现问题:

程序在对e[k〕的每一次赋值之前都要进行一次判断,而不是我们预想的在改变e[k]时才

进行判重。我们用一个布尔型的局部变量First来记录是否是对e[k]进行第一次赋值。修

改后的程序如下:

PROCEDUREWork(k);

BEGIN

First←True;

IFk=m十1

THEN打印结果

ELSEFORi←1TO字符串长度nDO

IF(i<>e〔1〕,e〔2〕…e[k-1]AND

(FirstORs〔i[<>s〔e[k]])

THEN

BEGIN

e[k]←i;

First←False;

Work(k十1);

END;

END;

很多选手的程序到此就为止了,可是它还有一个致命的错误:我们在判重时假定这个

字符串中的字符已经排好顺序,即相同的字符已经连在一起。事实上并不是这样,输入的

字符串中的字符排列是任意的,需要我们在递归之前作一次排序的初始化才能保证程序

运行得正确。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值