1. 程式人生 > 實用技巧 >Longest palindrome subsequence

Longest palindrome subsequence

問題

來自於《Introduction to algorithm》(CLRS) Problem 15-2

A palindrome is a nonempty string over some alphabet that reads the same forward and backward. Examples of palindromes are all strings of length 1, civic, racecar,and aibohphobia (fear of palindromes). Give an efficient algorithm to find the longest palindrome that is a subsequence of a given input string. For example, given the input character, your algorithm should return carac. What is the running time of your algorithm?

Input

  • X = <x1, x2, .... , xm>

Output

  • the longest palindrome of X

呼叫LCS演算法

針對這個問題,完全可以使用LCS最長公共子序列問題的演算法進行求解,將所給字串A進行倒置得到字串B,計算A,B的最長公共子序列即可。

動態規劃演算法

引用:演算法導論 — 思考題15-2 最長迴文子序列

優化子結構

Theorem

假設P[1..k]=< p1, p2 ,…,pk> 是字串S[1..n]=s1, s2 ,…, sn的一個最長迴文子序列,那麼:

  • if s1 == sn, then s1 = p1, sn = pn, P[2..k-1]是S[2..-1]的一個最長迴文子序列
  • if s1 != sn, s1 != p1, then P[1..k]是S[2..k]的一個最長迴文子序列
  • if s1 != sn, s1 != pn, then P[1..k]是S[1..k-1]的一個最長迴文子序列

這一問題的優化子結構,及其類似於LCS問題的優化子結構,都需要依據情況進行想處理。

Proof

  • 如果s1 != p1(s2 != p2這兩個其實是一個意思),那麼,完全可以將s1,s2加在P[1..k]兩側,構造一個比當前最長迴文子序列更長的迴文子序列。
  • 如果P[2..k-1]不是S[2..k-1]的一個最長迴文子序列,即存在迴文子序列Q是S[2..k-1]的一個最長迴文子序列,且,|Q| > k-2。那麼,可以在Q兩端加入s1==sn,得到迴文子序列M,|M| > k,這與P[1..k]=< p1, p2 ,…,pk> 是字串S[1..n]=s1, s2 ,…, sn的一個最長迴文子序列矛盾,假設不成立。
  • 至於2&&3點,反證法及其簡單。

遞迴地定義代價方程

定義c[i,j ]為s[i,j] = <si, si+1,..., sj>的最長迴文子序列的長度。

\[c[i,j]= \left\{ \begin{array}{rcl} & 0 & j < i \\ & 1 & j = i \\ & c[i+1,j-1] & j>i,s_{i} = s_{j} \\ & max{c[i,j-1],c[i+1,j]} & j>i,s_{i}\neq s_{j} \end{array} \right. \]

重疊子問題

要計算 需計算 需計算 需計算
c[i,j] c[i+1,j-1] c[i,j-1] c[i+1,j]
c[i+1,j-1] c[i+2,j-2] c[i+1,j-2] c[i+2,j-1]
c[i,j-1] c[i+1,j-2] c[i,j-2] c[i+1,j-1]
c[i+1,j] c[i+2,j+1] c[i+1,j-1] c[i+2,j]

計算優化解的代價的虛擬碼

下圖為優化解的填充順序,黃》綠》藍》橘》灰》白

由此可以構建出計算優化解的演算法(bottomup):

LPS_LENTH(x)
n = x.length
let s[1..n,1..n] and c[1..n,1..n]be a new array
for i = 1 to n do
    s[i,i] = 1; 
    s[i,i-1] = 0;
for l = 1 to n - 1 do 
    for i = 1 to n - l
        j = i + l
        if xi == xj
            s[i,j] = s[i+1,j-1] + 2
            c[i,j] = '↙'
        else 
            if s[i+1,j] > s[i,j-1]
               s[i,j] = s[i+1,j]
               c[i,j] = '↓'
            else
               s[i,j] = s[i,j-1]
               c[i,j] = '←'

構造優化解的虛擬碼

Print_LPS(x,c,i,j)
if i == j 
    print x[i]
    return
if c[i,j] == '↙'
    print x[i];
    Print_LPS(x,c,i+1,j-1);
    print x[i];
else if c[i,j] == '↓'
    Print_LPS(x,c,i+1,j);
else if c[i,j] = '←'
    Print_LPS(x,c,i,j-1);

演算法複雜度分析

計算優化解的代價的演算法時間複雜度為:O(n2)

構造優化解演算法的時間複雜度為:O(n)

程式碼

#include<stdio.h>
#include<string.h>

#define N 9

void LPS_LENGTH(char* x);
void Print_LPS(char* x, char c[N + 1][N + 1], int i, int j);

void main() {
	char x[N + 1] = { ' ', 'c', 'h', 'a', 'r', 'a', 'c', 't','e','r' };
	for (int i = 1; i <= N; i++) {
		printf("%c	", x[i]);
	}
	printf("\n");
	
	LPS_LENGTH(x);

}

void LPS_LENGTH(char* x) {
	int s[N + 1][N + 1];
	char c[N + 1][N + 1];
	memset(s, 0, sizeof(int) * (N + 1) * (N + 1));
	memset(c, '0', sizeof(char) * (N + 1) * (N + 1));
	for (int i = 1; i <= N; i++) {
		s[i][i] = 1;
		s[i][i - 1] = 0;
	}
	for (int l = 1; l <= N - 1; l++) {
		for (int i = 1; i <= N - l; i++) {
			int j = i + l;
			if (x[i] == x[j]) {
				s[i][j] = s[i + 1][j - 1] + 2;
				//c[i][j] = '↙';
				c[i][j] = '1';
			}
			else {
				if (s[i + 1][j] > s[i][ j - 1]) {
					s[i][j] = s[i + 1][j];
					//c[i][j] = '↓';
					c[i][j] = '2';
				}
				else {
					s[i][j] = s[i][j - 1];
					//c[i][j] = '←';
					c[i][j] = '3';
				}
			}
		}
	}
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++)
			printf("%d	", s[i][j]);
		printf("\n");
	}
	printf("\n");
	printf("\n");
	for (int i = 1; i <= N; i++) {
		for (int j = 1; j <= N; j++)
			printf("%d	", s[i][j]);
		printf("\n");
	}
	printf("\n");
	printf("\n");
	Print_LPS(x, c, 1, N);
}

void Print_LPS(char *x, char c[N+1][N+1], int i, int j) {
	if (i == j) {
		printf("%c	", x[i]);
		return;
	}
	if (c[i][j] == '1') {
		printf("%c	", x[i]);
		Print_LPS(x, c, i + 1, j - 1);
		printf("%c	", x[i]);
	}
	else if(c[i][j] == '2')
		Print_LPS(x, c, i + 1, j);
	else 
		Print_LPS(x, c, i, j - 1);
}