動態規劃解最長迴文子序列並優化空間複雜度

小飛_Xiaofei發表於2013-11-19

版權所有。所有權利保留。

歡迎轉載,轉載時請註明出處:

http://blog.csdn.net/xiaofei_it/article/details/16813591

一個字串有許多子序列,比如字串abcfgbda,它的子序列有a、bfg、bfgbd,在這些子序列中肯定有迴文字串。現在要對任意字串求其最長的迴文子序列。注意,本文不是解決最長迴文子串,迴文子串是連續的,迴文子序列是不連續的。

字串abcfgbda的最長迴文子序列為abcba,長度為5。

輸入:包含若干行,每行有一個字串,字串由大小寫字母構成,長度不超過100。

輸出:對每個輸入,輸出一行,該行有一個整數,表示最長迴文子序列的長度。

Example

Input:

a

abcfgbda

Output:

1

5


該題採用動態規劃思想。

對任意字串,如果頭和尾相同,那麼它的最長迴文子序列一定是去頭去尾之後的部分的最長迴文子序列加上頭和尾。如果頭和尾不同,那麼它的最長迴文子序列是去頭的部分的最長迴文子序列和去尾的部分的最長迴文子序列的較長的那一個。

設字串為s,f(i,j)表示s[i..j]的最長迴文子序列。

狀態轉移方程如下:

當i>j時,f(i,j)=0。

當i=j時,f(i,j)=1。

當i<j並且s[i]=s[j]時,f(i,j)=f(i+1,j-1)+2。

當i<j並且s[i]≠s[j]時,f(i,j)=max( f(i,j-1), f(i+1,j) )。

注意如果i+1=j並且s[i]=s[j]時,f(i,j)=f(i+1,j-1)+2=f(j,j-1)+2=2,這就是“當i>j時f(i,j)=0”的好處。

由於f(i,j)依賴i+1,所以迴圈計算的時候,第一維必須倒過來計算,從s.length()-1到0。

最後,s的最長迴文子序列長度為f(0, s.length()-1)。

程式碼如下:

#include <iostream>
#include <cstring>
using namespace std;

#define MAX 101
#define max(a,b) (a)>(b)?(a):(b)

int main()
{
	string s;
	while (cin>>s)
	{
		int f[MAX][MAX];
		memset(f,0,sizeof(f));
		for (int i=s.length()-1;i>=0;i--)
		{
			f[i][i]=1;
			for (int j=i+1;j<s.length();j++)
				if (s[i]==s[j])
					f[i][j]=f[i+1][j-1]+2;
				else
					f[i][j]=max(f[i][j-1],f[i+1][j]);
		}
		cout<<f[0][s.length()-1]<<endl;
	}
	return 0;
}

空間複雜度為O(n^2)。

進一步減少記憶體使用,我們發現計算第i行時只用到了第i+1行,這樣我們便不需要n行,只需要2行即可。

起初先在第0行計算f[s.length()-1],然後用第0行的結果在第1行計算f[s.length()-2],再用第1行的結果在第0行計算f[s.length()-3],以此類推。正在計算的那行設為now,那麼計算第now行時,就要用第1-now行的結果。這種方法很巧妙。

當計算完成時,如果s.length()是奇數,則結果在第0行;如果是偶數,則結果在第1行。

此空間複雜度為O(n)。

程式碼如下:

#include <iostream>
#include <cstring>
using namespace std;

#define MAX 101
#define max(a,b) (a)>(b)?(a):(b)

int main()
{
	string s;
	while (cin>>s)
	{
		int f[2][MAX];
		memset(f,0,sizeof(f));
		int now=0;
		for (int i=s.length()-1;i>=0;i--)
		{
			f[now][i]=1;
			for (int j=i+1;j<s.length();j++)
				if (s[i]==s[j])
					f[now][j]=f[1-now][j-1]+2;
				else
					f[now][j]=max(f[now][j-1],f[1-now][j]);
			now=1-now;
		}
		if (s.length()%2==0)
			cout<<f[1][s.length()-1]<<endl;
		else
			cout<<f[0][s.length()-1]<<endl;
	}
	return 0;
}

相關文章