最長迴文子串--動態規劃(O(N^2))

給定一個字符串 s,找到 s 中最長的迴文子串。你可以假設 s 的最大長度爲1000。

示例 1:

輸入: “babad”
輸出: “bab”
注意: "aba"也是一個有效答案。
示例 2:

輸入: “cbbd”
輸出: “bb”
迴文串的定義我就不囉嗦了。對於這道題,我的第一反應是用動態規劃方法解。

假設字符串s的長度爲length,建立一個length*length的矩陣dp。

dp[i][j]表示“以s[i]開始s[j]結尾的迴文串的長度。如果這個字符串不是迴文串,讓dp[i][j]=0”。顯然,j>=i,只需往dp填j>=i的部分即可。

dp[i][j]的遞推公式可以這麼表述:

(1)首先對dp的對角線元素初始化爲1,也就是當i==j時,dp[i][j]=1。

這很顯然,每個單獨的字符其實就是個長度爲1的迴文串。

(2)當j-i==1:

若s[i]==s[j],則dp[i][j]=2;否則dp[i][j]=0。

解釋:當j-i==1時,若s[i]==s[j],則s[i]和s[j]可以組成一個長度爲2的迴文串。若s[i]!=s[j],顯然他們不可能組成迴文串,dp[i][j]=0。

(3)當j-i>=2:

若s[i]==s[j]:若dp[i+1][j-1]>0,則dp[i][j]= dp[i + 1][j - 1] + 2;否則dp[i][j]= 0;

若s[i]!=s[j]:dp[i][j]=0。

解釋:如果s[i]==s[j],表明這個子串有可能是迴文串。當去頭去尾後它是迴文串時,就可以在去頭去尾的那個迴文串長度基礎上+2,得到它的長度。如果去頭去尾後不是迴文串,那這個子串一定不是迴文串,迴文串長度只能是0。

若s[i]!=s[j],顯然他們不可能組成迴文串,dp[i][j]=0。

只需找到dp[i][j]的最大元素和它對應的i和j就可以得到結果“s中最長迴文子串”。

另外還有一個要注意的點:因爲需要訪問dp[i+1][j-1],因此i是從大到小的,j是從小到大的。j從0到size-1,i從j-1到0。

#include "iostream"
#include "string"
#include "sstream"
#include "vector"
#include "algorithm"
using namespace std;

int main()
{
	string str =" ";
	
	while (getline(cin, str))
	{
		int len = str.length();
		vector<vector<int>> dp(len + 1, vector<int>(len + 1, 0)); //dp[i][j]表示字符串第i個開始和字符串第j個結束之間的迴文長度
		for(int i = 1; i < len + 1; i++)
			for (int j = 1; j < len + 1; j++)
			{
				if (i == j)
					dp[i][j] = 1;        //對角線的元素置爲1,因爲單獨字符的迴文長度爲1
			}
		int max_len = 0;
		int start = 0;
		for(int j = 1; j< len+1 ; j++)//以結束爲止爲外循環,每次循環完畢則得到以此爲終點的最長迴文串
			for (int i = j - 1; i >= 1; i--)
			{
				
				if (str[i - 1] == str[j - 1])//起、終元素一樣
				{
					if (j - i == 1)          //如果長度差1
						dp[i][j] = 2;
					else
					{
						if (dp[i + 1][j - 1] > 0) //長度差大於2,就看是一個串是不是迴文
							dp[i][j] = dp[i + 1][j - 1] + 2;
						else
							dp[i][j] = 0;
					}
				}
				else
					dp[i][j] = 0;
				if (dp[i][j] > max_len)
				{
					max_len = dp[i][j];
					start = i-1;
				}
			}
		string s = str.substr(start, max_len);
		cout << s << endl;
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章