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原创,转载请注明出处)