poj 1159 Palindrome ( dp 滚动数组 )

本文介绍了一种算法,用于计算将任意字符串转换为回文字符串所需的最小字符插入数量,并提供了两种动态规划方法的具体实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Palindrome
Time Limit: 3000MS Memory Limit: 65536K
Total Submissions: 52945 Accepted: 18265

Description

A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a program which, given a string, determines the minimal number of characters to be inserted into the string in order to obtain a palindrome. 

As an example, by inserting 2 characters, the string "Ab3bd" can be transformed into a palindrome ("dAb3bAd" or "Adb3bdA"). However, inserting fewer than 2 characters does not produce a palindrome. 

Input

Your program is to read from standard input. The first line contains one integer: the length of the input string N, 3 <= N <= 5000. The second line contains one string with length N. The string is formed from uppercase letters from 'A' to 'Z', lowercase letters from 'a' to 'z' and digits from '0' to '9'. Uppercase and lowercase letters are to be considered distinct.

Output

Your program is to write to standard output. The first line contains one integer, which is the desired minimal number.

Sample Input

5
Ab3bd

Sample Output

2

Source



大致题意:

给出字符串和字符串的长度,问该字符串变为回文字符串最少需要添加的字符数。


分析:

做这题有两种动态规划的方法,一是直接对它和它的逆序串求最长公共子序列长度len。n-len即为所求。(n为原串长度)。

另外一种动态规划的做法是

dp[i][j]表示从 i 到 j 这段子串若变为回文串最少添加的字符数。

if (st[i] == st[j])
                dp[i][j] = dp[i + 1][j - 1];
else
                dp[i][j] = min(dp[i + 1][j], dp[i][j - 1]) + 1;

但是两种方法都需要用到滚动数组节约内存!!



因为题目给的是65536KB的内存,只能开1677W的int数组,而5000*5000的dp数组有2500W显然超内存了。会TLE的。但如果用short int就可以过。。(因为目前流行的32位C++编译器中,通常int占4字节,short int占2字节。其中short int可以简写为short。)

但还是要学一下滚动数组。。。

根据dp滚动的过程我们可以知道,dp【i】【j】的值不会与dp[i-2][0.....n]的值有关系。

那么可以把dp[i][j]的值覆盖到dp[i-2][j]上。即dp[i][j]为dp[i%2][j];


下面的法一是用动态数组和LCS的。法二是用short int和直接dp求子串变为回文序列最少需要添加的字符数的。



法一:


#include 
  
   
#include 
   
    
#define max 5010
int dp[2][max];//dp[i][j]表示正序的前i个(0~i-1)(这里还要取余2)和逆序的前j个(0~j-1)的最长公共子串长度。
char s1[max],s2[max];
int main()
{
	int n;
	while( scanf("%d",&n) != EOF)
	{
		scanf("%s",s1);
		int len = strlen(s1);
		for(int i = len - 1; i >= 0; i--)
		{
			s2[len - i - 1] = s1[i];
		}
		s2[len] = '\0';
		memset(dp,0,sizeof(dp));//全部初始化为0
		for( int i = 1; i <= len; i++ )
		{
			for( int j = 1; j <= len; j++ )
			{
				if(s1[i-1] == s2[j-1])
					dp[i%2][j] = dp[(i-1)%2][j-1] + 1;
				else 
					dp[i%2][j] = dp[(i-1)%2][j] > dp[i%2][j-1] ? dp[(i-1)%2][j] : dp[i%2][j-1];
			}
		}
	 	int ans = n - dp[n%2][n];
		printf("%d\n",ans);
	}
}

   
  



法二:


#include 
  
   
#include 
   
    
#include 
    
     
#include 
     
      
using namespace std;

#define maxn 5010

char s[maxn];
int n;
short dp[maxn][maxn];//dp[i][j]表示从i到j这段子串若变为回文串最少需要添加的字符数

int main()
{
    scanf("%d", &n);
    scanf("%s", s);
    for (int i = n -1; i >=0; i--)//从最后往前推
    {
        dp[i][i] =0;
        for (int j = i +1; j < n; j++)
        {
            if (s[i] == s[j])
            {
                dp[i][j] = dp[i +1][j -1];//往内压缩,i+1 ~ n-1的肯定已经计算完了
            }	
            else
            {
                dp[i][j] = min(dp[i +1][j], dp[i][j -1]) +1;//i是逆推的,所以i+1已经计算好了,
                                                           //j是顺推的,所以j-1已经计算好了。
            }	
        }
    }
    printf("%d\n", dp[0][n -1]);
    return 0;
}

     
    
   
  





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值