您的位置:首页 > 其它

poj1159

2016-01-14 20:14 141 查看
涉及算法: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

;
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]
);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: