最大回文子序列,例如cafgfkc,最大回文子序列cfgfc,输出5。子序列相当于删除某些位置上的字符后形成的序列。
最大回文子串,例如cafgfkc,最大回文子串fgf,输出3。子串相当于截取start位到end位的子串。
试过没认真看题目,原题是求子序列,想当然以为求子串,直接高高兴兴用manacher开写O(n)的实现,写正确完整后才惊喜发现是求子序列。悲剧。。
最大回文子序列: 动态规划实现。dp[i][j] 表示考虑 i 到 j 的子串时的最大回文子序列,对任何 i,dp[i][i]=1
样例输入:
cafgfkc
ksdfefrds
样例输出:
5
7
C++代码:
#include<iostream>
#include<string.h>
using namespace std;
#define rep(i,s) for(int i=0;i<s;i++)
#define mst(s,t) memset(s,t,sizeof(s))
#define MAXN 1002
int dp[MAXN][MAXN];
int len;
char str[MAXN];
int tmax(int a,int b,int c){
a=a>b?a:b;
a=a>c?a:c;
return a;
}
int dpfs(int s,int t){
if(dp[s][t]!=0||s>=t)
return dp[s][t];
if(str[s]==str[t])
dp[s][t]=dpfs(s+1,t-1)+2;
dp[s][t]=tmax(dpfs(s+1,t),dpfs(s,t-1),dp[s][t]);
return dp[s][t];
}
int main(){
while(cin>>str){
mst(dp,0);
len=strlen(str);
for(int i=0;i<len;i++){
dp[i][i]=1;
}
cout<<dpfs(0,len-1)<<endl;
}
}
最大回文子串
样例输入:
cafgfkc
ksdfefrds
样例输出:
3
3
很久以前用java写过manacher-O(n),就直接上java代码了。其实还可以用动态规划-O(n^2),或者中心扩展法-O(n^2)(manacher是这种算法的拓展优化)
java代码:
import java.util.Scanner;
public class MaxPlalindrome{
static String str;
static String str1;
static int nlen;
static int[] p=new int[1000001];//p[i]为以第i+1个字符为中心的最大回文数
public static void main(String[] args){
Scanner s=new Scanner(System.in);
while(s.hasNext()){
str=s.next();
nlen=Proc(str);
Manacher(str1,nlen);
int ans=1;
for(int i=0;i<nlen;i++){
ans=Math.max(ans, p[i]);
}
System.out.println(ans-1);
}
}
public static int Proc(String str){
StringBuilder sk=new StringBuilder("$");
int len=str.length();
for(int i=0;i<len;i++){
sk.append('#');sk.append(str.charAt(i));
}
sk.append('#');str1=sk.toString();
return len*2+1;
}
public static void Manacher(String st,int len){
int mx=0,id=0;
for(int i=0;i<len;i++){
p[i]=0;
}
//一个回文是左右对称的,那么在计算右边范围内的某个字符,
//计算这个字符为中心的回文长度的时候,就可以利用他对称的左边的字符的回文长度
for(int i=0;i<len;i++){
p[i]=mx>i?Math.min(p[2*id-i], mx-i):1;
while(i+p[i]<=len&&i-p[i]>=0&&st.charAt(i+p[i])==st.charAt(i-p[i]))
p[i]++;
if(i+p[i]>mx){
mx=i+p[i];
id=i;
}
}
}
}