最短回文串 题解

 最短回文串(palindrome.pas/c/cpp)

    如果一个字符串正过来读和倒过来读是一样的,那么这个字符串就被称作回文串。例如abcdcba,abcddbca就是回文串,而abcdabcd不是。

  你要解决的问题是:对于任意一个字符串,输出将这个字符串变为回文串需要插入的最少字符个数,比如,Ab3bd只需要插入2个字符就可以变为一个回文串。

 

输入数据

    第一行是一个整数N

    第二行是一个长度为N字符串S

 

输出数据

    一行一个整数,表示将S变为回文串需要插入的最小字符个数

 

样例输入与输出

5

Ab3bd
 2 

数据范围

    对于所有数据,0

有两种方法:    方法一: 若A形如?A'?,(问号代表任意一个相同字符,下同)则只需将A'变为回文串。

   若A形如?A'或者A'?,则只需将A'变为回文串,再在A的后面或者前面插入一个”?”

 

定义状态f[i,j]为将Ai..Aj变为回文串的最小代价,则

f[i][j]=    f[i+1][j-1] (若Ai=Aj)

min(f[i+1][j],f[i][j-1])+1 (若Ai<>Aj)

一共有n2个状态,状态转移是O(1)的,总的复杂度为O(n2)

 


方法二:

另一种方法是将原串与原串的倒序做一次LCS,用原串长度减去LCS长度,即为需要插入字符的个数。

lcs即为最长公共子序列:

 

n用c[i,j]表示X[1..i]和Y[1..j]的最长公共子序列的长度。
n          0                                  i=0或j=0
c[i,j]=  c[i-1,j-1]+1                i,j>0且X[i]=Y[j]
          max{c[i-1,j] , c[i,j-1]} i,j>0且X[i]≠Y[j]
n观察上式发现,为了求c[i,j],需要预先知道c[i-1,j-1]、c[i-1,j]和c[i,j-1]。所以可以先从小到大枚举i,再从小到大枚举j。
n最长公共子序列的长度就是c[m,n]。 
 
还有方法三 是来自复旦附中,进入清华的李鲁鲁同学提供的:用KMP匹配,复杂度O(n),可惜我不会.......
 
c:方法一 

#include int f[5001][5001]; int n; char seq[5001]; void init(void) { freopen("palindrome.in","r",stdin); freopen("palindrome.out","w",stdout); scanf("%d/n",&n); fgets(seq,6000,stdin); } void search(int l,int r) { if(f[l][r]!=-1) return; if(l>=r) { f[l][r]=0; return; } if(seq[l]==seq[r]) { search(l+1,r-1); f[l][r]=f[l+1][r-1]; } else { search(l+1,r); search(l,r-1); f[l][r]=f[l+1][r]+1; if(f[l][r-1]+1

 

pascal:方法二:

var i,j,k,m,n:longint; a,b:array[1..100000] of char; ch:char; c:array[1..10000,1..10000] of longint; begin assign(input,'palin1.in');reset(input); assign(output,'palindrome.out');rewrite(output); readln(n); for i:=1 to n do begin read(ch); a[i]:=ch; b[n-i+1]:=ch; end; for i:=1 to n do begin for j:=1 to n do begin if a[i]=b[j] then c[i,j]:=c[i-1,j-1]+1 else if c[i-1,j]>=c[i,j-1] then c[i,j]:=c[i-1,j] else c[i,j]:=c[i,j-1]; end; end; writeln(n-c[n,n]); close(input);close(output); end.    
 

 

你可能感兴趣的:(题解)