LCS应用—回文词

      刚刚介绍了弱小的LCS(最长公共子串),现在介绍一个它强大功能的应用。

      首先看一下题目:

 

 

【题目描述】(vijos1327)
回文词是一种对称的字符串——也就是说,一个回文词,从左到右读和从右到左读得到的结果是一样的。任意给定一个字符串,通过插入若干字符,都可以变成一个回文词。你的任务是写一个程序,求出将给定字符串变成回文词所需插入的最少字符数。 
比如字符串“Ab3bd”,在插入两个字符后可以变成一个回文词(“dAb3bAd”或“Adb3bdA”)。然而,插入两个以下的字符无法使它变成一个回文词。

【输入格式】
第一行包含一个整数N,表示给定字符串的长度,3<=N<=5000 
第二行是一个长度为N的字符串,字符串由大小写字母和数字构成。 

【输出格式】
一个整数,表示需要插入的最少字符数。

【样例输入】
5
Ab3bd

【样例输出】
2

 

 

      这道题咋看上去MS与我们的LCS没有什么关系,很简单,LCS有两个串,而这题只有一个*.*||。但是再看一遍题目,我们就会发现它们还是有一些相同点的(囧)——都是找相同的部分。这样,只要把原串S倒过来,变成S1,我们就有两个串了^.^。然后对这两个串求一遍LCS(终于派上用场了),我们就找到了这一个串可以配对的最大字母数(若串长是奇数,那么中间的数也算)。然后用总串长减去可以配对的字母数就是要求的插入字符数。

      如果还是迷迷糊糊,下面的图示或许会告诉你答案。

      以样例为例:

 

S(原串)       A   b  3  b  d

S1(倒序串)   d   b  3  b  A

LCS                   b  3  b

 

      所以,有3个字符已经配对,不用添加字符就能够成回文,需要添加的字母仅有“A”与“d”。

      有的同学问我这样一个弱小的问题:为什么奇数长串的中间的一个字符也被纳入“已配对”的范畴,当插入新字符时它的位置可能移动。注意,左右两边未配对的字符的数目一定是相等的(可以找几种情况试一试)。所以,中间的字符是不会变的。

 

      参考代码:

 

1 program palindrome;
2 var
3 a,b:array[1..1000]of char;
4 f:array[0..1000,0..1000]of longint;
5 n,i,j:integer;
6 function max(x,y:longint):longint;
7 begin
8 if x>y then exit(x)
9 else exit(y);
10 end;
11 begin
12 readln(n);
13 for i:=1 to n do
14 begin
15 read(a[i]);
16 b[n-i+1]:=a[i]; //制作S2
17 end;
18 for i:=1 to n do //LCS
19 for j:=1 to n do
20 if a[i]=b[j] then
21 f[i,j]:=f[i-1,j-1]+1
22 else f[i,j]:=max(f[i-1,j],f[i,j-1]);
23 writeln(n-f[n,n]); //输出需要配对的字符数
24 end.
 

 

 

 

(saltless原创,转载请注明出处)

posted on 2010-08-09 14:38  saltless  阅读(927)  评论(0编辑  收藏  举报

导航