LeetCode 516.最長迴文子序列
區間型動態規劃
確定狀態:
最優策略產生的最長迴文子串是T
,長度M
。
情況一:迴文串長度爲1,即一個字母。
情況二:迴文串長度大於1,那麼必定有T[0]=T[M-1]
設T[0]
是S[i]
,T[M-1]
是S[j]
,剩下的T[1...M-2]
仍然是迴文串,而且是S[i+1...j-1]
的最長迴文子串。
狀態: 設f[i][j]
爲S[i..j]
的最長迴文子串的長度。
轉移方程:
f[i][j]=max{f[i+1][j],f[i][j-1],f[i+1][j-1]+2(S[i]==S[j])}
初始條件:
一個字母也是一個長度爲1的迴文串:f[0][0]=f[1][1]=...=f[N-1][N-1]=1
如果S[i]==S[i+1]
,f[i][i+1]=2
否則f[i][i+1]=1
計算順序:
區間型動態規劃:按照j-i從小到大的順序計算
長度1:f[0][0],f[1][1]....f[n-1][n-1]
長度2:f[0][1],f[1][2]....f[n-2][n-1]
…
長度N:f[0][N-1]
class Solution {
public int longestPalindromeSubseq(String s) {
char []ch=s.toCharArray();
int n=ch.length;
int [][]f=new int [n][n];
// length:1
for(int i=0;i<n;i++)
{
f[i][i]=1;
}
//length:2
for(int i=0;i<n-1;i++)
{
f[i][i+1]= (ch[i]==ch[i+1])?2:1;
}
//length >2
//Enumeration length
for(int len=3;len<=n;len++)
{
//IMPORTANT: Give attention to the border is equal to n-length
//Example: len=3
//0,1,2,3....n-3,n-2,n-1
// |
// Finish
for(int i=0,j;i<=n-len;i++)
{
j=i+len-1;
//f[i][j]=MAX{f[i+1][j],f[i][j-1],f[i+1][j-1]+2}
f[i][j]=Math.max(f[i+1][j],f[i][j-1]);
if(ch[i]==ch[j])
f[i][j]=Math.max(f[i][j],f[i+1][j-1]+2);
}
}
return f[0][n-1];
}
}
拓展:輸出最長迴文子串
用pi[][]
用來記錄每一步的操作,從兩側向內掃描。
public int longestPalindromeSubseq(String s) {
char []ch=s.toCharArray();
int n=ch.length;
int [][]f=new int [n][n];
int [][]pi=new int[n][n];
// length:1
for(int i=0;i<n;i++)
{
f[i][i]=1;
}
//length:2
for(int i=0;i<n-1;i++)
{
f[i][i+1]= (ch[i]==ch[i+1])?2:1;
}
//length >2
//Enumeration length
for(int len=3;len<=n;len++)
{
for(int i=0,j;i<=n-len;i++)
{
j=i+len-1;
f[i][j]=Math.max(f[i+1][j],f[i][j-1]);
if(f[i][j]==f[i+1][j])
pi[i][j]=0;
else if(f[i][j]==f[i][j-1])
pi[i][j]=1;
if(ch[i]==ch[j])
{
f[i][j]=Math.max(f[i][j],f[i+1][j-1]+2);
pi[i][j]=2;
}
}
}
char []res=new char[f[0][n-1]];
int p=0,q=f[0][n-1]-1;
int i=0,j=n-1;
while(i<=j)
{
if(i==j)
{
res[p]=ch[i];
break;
}
if(i+1==j)
{
res[p]=ch[i];
res[q]=ch[j];
break;
}
if(pi[i][j]==0) i++;
if(pi[i][j]==1) j--;
if(pi[i][j]==2)
{
res[p++]=ch[i++];
res[q--]=ch[j--];
}
}
System.out.println(String.valueOf(res));
return f[0][n-1];
}
記憶化搜索:
將遞歸每個計算過的結果保存。
class Solution {
int [][]f=null;
char []ch=null;
int n=0;
private void compute(int i,int j)
{
if(f[i][j]!=-1)
return;
if(i==j)
{
f[i][j]=1;
return;
}
if(i+1==j)
{
f[i][j]=(ch[i]==ch[j])?2:1;
return;
}
compute(i+1,j);
compute(i,j-1);
compute(i+1,j-1);
f[i][j]=Math.max(f[i+1][j],f[i][j-1]);
if(ch[i]==ch[j])
{
f[i][j]=Math.max(f[i][j],f[i+1][j-1]+2);
}
}
public int longestPalindromeSubseq(String s) {
ch=s.toCharArray();
n=ch.length;
if(n==0)
return 0;
if(n==1)
return 1;
f=new int [n][n];
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
f[i][j]=-1;
}
}
compute(0,n-1);
return f[0][n-1];
}
}