poj1159

涉及算法:dp+lcs
题目大意:对于给定的字符串S,求出最少需要在S中添加多少个字符可以使S对称(我们称此事S为回文串),即从左往右看s和从右往左看S是一样的,
题目分析:两种思路
思路一:设dp[i][j]为字符串S从第个字母到第j个字母S之间的这段字符串S(i,j)需要填加的字符数,使得S(i,j)为回文串。

思路二:S中需要填加的字符数等于:S的长度-S和其逆序列S’的最长公共子序列的长度。用最长公共子序列的知识来求解

还有一个问题,因为S的长度可以高达500,那么势必需要一个5000x5000的二维数组,空间开销十分巨大,所以这里用了滚动数组来节省空间开销

代码如下:

import java.util.Scanner;

public class Main_1159 {

	static int n;
	static char[] a;
	static short[][] dp;
	
	public static void main(String[] args) {
		
		Scanner in=new Scanner(System.in);
		n=in.nextInt();
		String s=in.next();
		a=s.toCharArray();
		
		dp4();

	}
	//思路一AC
	static void dp(){
		dp=new short[n][n];
		for(int i=0;i<n;i++){
			for(int j=i;j>=0;j--){
				dp[i][j]=0;
			}
		}
		
		//i要从右边开始j要从左边开始,这取决于转移方程:dp[i][j]=dp[i+1][j-1];
		for(int i=n-2;i>=0;i--){
			for(int j=i+1;j<n;j++){
				if(a[i]==a[j]){
					dp[i][j]=dp[i+1][j-1];
				}else {
					dp[i][j]=(short) Math.min(dp[i+1][j]+1, dp[i][j-1]+1);
				}
			}
		}
		
		System.out.println(dp[0][n-1]);
	}
	
	//思路一+滚动数组
	//dp[i][j]=dp[i+1][j-1]改写成dp[i%2][j]=dp[(i+1)%2][j-1]
	static int[][] dp2=new int[2][5001];
	static void dp2(){
		
		for(int i=n-1;i>=0;i--){
			
			for(int j=0;j<n;j++){
				if(i>=j){
					dp2[i%2][j]=0;
				}else {
					if(a[i]==a[j]){
						dp2[i%2][j]=dp2[(i+1)%2][j-1];
						
					}else {
						dp2[i%2][j]=Math.min(dp2[(i+1)%2][j]+1, dp2[i%2][j-1]+1);
					}
				}
				
			}
		}
		System.out.println(dp2[0][n-1]);
	}
	
	
<span style="white-space:pre">	</span>//思路二:
	//用Lcs来解dp[i][j]:表示s[0,i-1]和s'[0,j-1]的最长公共子序列,s'为s的逆序列
	static char[] b=new char[5000];//b为a的逆序列
	static void dp4(){
		for(int i=0;i<n;i++){
			b[i]=a[n-i-1];
		}
		for(int i=0;i<=n;i++){
			dp2[0][i]=0;
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(a[i-1]==b[j-1]){
					dp2[i%2][j]=(short) (dp2[(i-1)%2][j-1]+1);
				}else {
					dp2[i%2][j]=(short) Math.max(dp2[(i-1)%2][j], dp2[i%2][j-1]);
				
				}
			}
		}
		System.out.println(n-dp2[n%2][n]);
	}
}



你可能感兴趣的:(poj1159)