最长回文子串 与 最长回文子序列

package main;

import java.util.Scanner;

public class Main {
	/*
	 * 最长回文子串与最长回文子序列区别
	 * (1)给一个字符串,找出它的最长的回文子序列的长度。例如,如果给定的序列是“BBABCBCAB”,则输出应该是7,“BABCBAB”是在它的最长回文子序列。 
	 *   “BBBBB”和“BBCBB”也都是该字符串的回文子序列,但不是最长的。
	 * (2)注意和最长回文子串的区别(参考:最长回文串)!这里说的子序列,类似最长公共子序列LCS( Longest Common Subsequence)问题,可以是不连续的。
	 * 	       这就是LPS(Longest Palindromic Subsequence)问题。
	 */
/***********************************求最长回文子串(方法一和方法二)*********************************************/	
	
	/*
	 *  方法一   动态规划法
	 *  比如P[i,j](表示以i开始以j结束的子串)是回文字符串,那么P[i+1,j-1]也是回文字符串
	 *  P[i,j]=false 表示子串[i,j]不是回文串。P[i,j]=true 表示子串[i,j]是回文串。
	 *  初始化 P[i,i]=1;
	 *         |- P[i+1, j-1], if(s[i]==s[j])
	 *  P[i,j]=|
	 *  	   |_ false, if(s[i]!=s[j])
	 */
	public static String longestPalindrome1(String s) {
		int maxLen = 0; //记录最长回文串的长度
		int start = 0; //记录最长回文串的开始位置
		boolean[][] P = new boolean[1000][1000];
		if(s.length()==1)
			return s;
		//初始化
		for(int i = 0;i<s.length();i++){
			P[i][i] = true; //P[i][j]表示以i开始,以j结束的子串
			if(i<s.length()-1&&s.charAt(i)==s.charAt(i+1)){
				P[i][i+1] = true;
				start = i;
				maxLen = 2;
			}
		}
		for(int len = 3;len<=s.length();len++){ //长度从3开始
			for(int i = 0;i<=s.length()-len;i++){ //设置初始位置
				int j = i+len-1;  //与i相对的位置
				//若在两端的位置i,j位置处的字符相等,再者从i+1~j-1之间为true,更新
				if(P[i+1][j-1]==true&&s.charAt(i)==s.charAt(j)){
					P[i][j] = true;
					maxLen = len;
					start = i;
				}
			}
		}
		//System.out.println("start: "+start+" maxLen:"+maxLen);
		if(maxLen>=2)
			return s.substring(start, start+maxLen);
		return null;
    }
	
	/*
	 * 方法二:中心扩展法
	 * 中心扩展就是把给定的字符串的每一个字母当做中心,向两边扩展,这样来找最长的子回文串。算法复杂度为O(N^2)
	 * 
	 */
	public static String longestPalindrome2(String s) {
		int maxLen = 0; //回文字串的最大长度
		int start = 0;  //回文字串的开始位置
		
		if(s.length()==1)
			return s;
		
		//s的长度为奇数
		for(int i = 0;i<s.length();i++){
			int j = i-1; //i的左边
			int k = i+1; //i的右边
			while(j>=0&&k<s.length()&&s.charAt(j)==s.charAt(k)){
				if(k-j+1>maxLen){
					maxLen = k-j+1;
					start = j;
				} //if
				j--;
				k++;
			} //while
		}
		
		//s的长度为偶数
		for(int i = 0;i<s.length();i++){
			int j = i; //i的左边
			int k = i+1; //i的右边
			while(j>=0&&k<s.length()&&s.charAt(j)==s.charAt(k)){
				if(k-j+1>maxLen){
					maxLen = k-j+1;
					start = j;
				} //if
				j--;
				k++;
			} //while
		}
		return s.substring(start, start+maxLen);
	}
	
	
/***********************************求最长回文子序列(方法一、方法二和方法三)*********************************************/	
	/*  方法一 动态规划:求最长回文子序列
	 * 对任意字符串,如果头和尾相同,那么它的最长回文子序列一定是去头去尾之后的部分的最长回文子序列加上头和尾。
	 *         如果头和尾不同,那么它的最长回文子序列是去头的部分的最长回文子序列和去尾的部分的最长回文子序列的较长的那一个。
     * 设字符串为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”的好处。
	 */
	public static int longestP1(String s) {
		int[][] f = new int[1000][1000];
		for(int i=s.length()-1;i>=0;i--){
			f[i][i] = 1;
			for(int j = i+1;j<s.length();j++){
				if(s.charAt(i)==s.charAt(j)){
					f[i][j] = f[i+1][j-1]+2;
				}else{
					f[i][j] = f[i][j-1]>f[i+1][j]?f[i][j-1]:f[i+1][j];
				} //if
			}
		}
		return f[0][s.length()-1];
	}
	
	//方法二,对方法一的改进,用不了那么多空间
	//如果s.length()是奇数,则结果在第0行;如果是偶数,则结果在第1行。
	public static int longestP2(String s) {
		int[][] f = new int[2][1000];
		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.charAt(i)==s.charAt(j)){
					f[now][j] = f[1-now][j-1]+2;
				}else{
					f[now][j] = f[now][j-1]>f[1-now][j]?f[now][j-1]:f[1-now][j];
				} //if
			}
			now = 1 - now;
		}
		if(s.length()%2==0)
			return f[1][s.length()-1];
		else
			return f[0][s.length()-1];
	}
	
	
	//方法三,好理解
	public static int longestP3(String s, int i, int j) {
		if(i==j)
			return 1;
		if(i>j)
			return 0;
		if(s.charAt(i)==s.charAt(j))
			return longestP3(s,i+1,j-1)+2;
		else
			return longestP3(s,i+1,j)>longestP3(s,i,j-1)?longestP3(s,i+1,j):longestP3(s,i,j-1);
	}
	
	

	public static void main(String[] args) {
		Scanner scan = new Scanner(System.in);
		while(scan.hasNext()){
			String s = scan.next();
			//求最长回文子串
			System.out.println("方法一:动态规划:"+longestPalindrome1(s));
			System.out.println("方法二:中心扩展法:"+longestPalindrome2(s));
			
			//求最长回文子序列
			System.out.println("方法一:动态规划-递归1:"+longestP1(s));
			System.out.println("方法二:动态规划-递归2:"+longestP2(s));
			System.out.println("方法三:动态规划-递归3:"+longestP3(s,0,s.length()-1));
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bboyzqh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值