问题
给定一个长度为n(<=5000)的字符串,求最少添加几个字符使之变成回文串
分析
设原序列S的逆序列为S',则这道题目的关键在于,
最少需要补充的字母数= 原序列S的长度— S和S'的最长公共子串长度
设 S 为原字符串,RS=reverse(S)即S反转后的字符串,LCS(S,RS)为S与RS的最长公共子序列,len(S)为S的长度。令S={a1~an},则RS={an~a1},S-LCS(S,RS)={b1~bn},这里的集合都是有序、元素可重复的;bi∈S,RS;且i≠j时,bi≠bj,否则有bi∈LCS(S,RS);同时LCS(S,RS)一定是回文,因为其是在S的正序与逆序下同时出现的子序列。由上,只需添加字符将S-LCS(S,RS)变成回文即可,设size=len(S-LCS(S,RS));因为其元素各不相同,此时有两种方案使之成为回文:1.添加size-1个字符,例如:c1c2c3 --> c1c2c3c2c1;2.添加size个;对于第一种,c3肯定是在回文中的中间位置,也必须是在LCS(S,RS)的中间位置,即c3∈LCS(S,RS),所以排除这种情况。于是最少需添加size个字符,即min=len(S)-len(S,RS)。
这个证明需要自己思考一下。
这道题由于空间需求很大,所以考虑用滚动数组优化。
由于i之和i中已更新的和i-1有关,所以只需2*5000就能解决问题,但是会使算法的时间复杂度接近n3,以时间换空间,所谓的求余滚动效率并不高,因为mod运算非常之慢。
code
program liukeke;
var
f:array[0..1,0..5001] of longint;
n,i,j:longint;
s1,s2:ansistring;
function max(a,b:longint):longint;
begin
if a>b then exit(a);
exit(b);
end;
begin
readln(n);
readln(s1);
for i:=1 to length(s1) do
s2:=s1[i]+s2;
for i:=1 to length(s1) do
begin
for j:=1 to length(s2) do
if s1[i]=s2[j] then
f[1,j]:=f[0,j-1]+1
else
f[1,j]:=max(f[0,j],f[1,j-1]);
f[0]:=f[1];
end;
writeln(length(s1)-f[1,length(s2)]);
end.
反思
要看清题目,不要一味套用经典算法,通过分析使之和经典算法建立联系。
考虑优化