添加最少字符数构成使字符串构成回文

来源:互联网 发布:监控网络键盘 编辑:程序博客网 时间:2024/05/21 07:02
FROM:


http://www.cnblogs.com/zhengyuhong/p/3641418.html

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

5Ab3bd

Sample Output

2

Source

IOI 2000
 
我看到这个题目第一时间是用递归算法直接做的,就几行代码,很简单的,但是提交,咦,超时哦,看来递归有很多重复工作,于是就想到用动态规划算法来解决这个问题。
算法如下:
记字符串为strleft为当前字符串最左边字符的索引下标,right则是当前字符串最右边字符的索引下标
1、如果字符串只有一个字符,返回0
2、如果字符串有两个字符,相同则返回0,否则返回1
3、字符串三个或者三个以上则有如下状态转移方程
    1)、str[left] == str[right];则minAdd(str,left,right) = minAdd(str,left+1,right-1)
    2)、str[left] != str[right];则minAdd(str,left,right) =1+ min( minAdd(str,left,right-1),minAdd(str,left+1,right) );
 
 
用m[][]记录left~right字符串构成回文所需的最少字符数,可以避免重复计算。
具体可以在代码中看到m[][]的初始化与构造。
复制代码
#include <iostream>using namespace std;const short N = 5001;short m[N][N];short  foo(char* str,short left,short right){    if(left  == right)    {        return m[left][right] = 0;    }    else if( left + 1 == right )    {        if( str[left] == str[right] )        {            return m[left][right] = 0;        }        else        {            return m[left][right] = 1;        }    }    else    {        if( m[left][right] != -1 )        {            return m[left][right];         }        else        {            if(str[left] == str[right])            {                return m[left][right] = foo(str,left + 1, right - 1);            }             else            {                short a = foo(str,left, right - 1) + 1;                short b = foo(str,left + 1, right) + 1;                return m[left][right] = a < b ? a : b;                }        }     }}int main(int argc, char** argv) {     char str[N];    short i, j, n;    for( i = 0; i < N; ++i)    {        for(j =0; j < N; ++j)        {            m[i][j] = -1;        }    }    cin>>n;    i = 0;     while(i < n)    {        cin>>str[i++];    }    str[i]=0;    cout<<foo(str,0,n - 1);     return 0;}
复制代码

本来二维数组m[N][N]是int型的,但是OJ报告内存超过限制,转用short即可,可以覆盖5000,因为题目要求是 2<N<5001,或者可以使用滚动数组,可以看如下的另一个代码。

复制代码
#include <iostream>#include <cstring>using namespace std;const int N = 5001;short m[2][N];short foo(char* str, short n){    short i, j, k, k1, k2, a, b;    for(k = 1; k < n; ++k)    {        k1 = k & 1;        k2 = (k - 1) & 1;        for(i = 0; i + k < n; ++i)        {            j = i + k;            if(str[i] == str[j])            {                m[k1][i] = m[k1][i+1];            }            else            {                m[k1][i] = 1 + (m[k2][i] < m[k2][i+1] ? m[k2][i] : m[k2][i+1]);             }        }    }    return m[(n-1)&1][0];}int main(int argc, char** argv) {     char str[N];    short i, n;    i = 0;     cin>>n;    while(i < n)    {        cin>>str[i++];    }    str[i]=0;    cout<<foo(str,n);    return 0;}
复制代码

在这里用到一个滚动数组,妙用了&1,就是轮换使用两个一维数组,降低了一个数量级的空间复杂度。

 

写一次代码就有些心得体会,看来算法还是得通过实现来巩固掌握。

0 0
原创粉丝点击