51Nod 1092 回文字符串 c/c++题解

题目描述

回文串是指aba、abba、cccbccc、aaaa这种左右对称的字符串。每个字符串都可以通过向中间添加一些字符,使之变为回文字符串。
例如:abbc 添加2个字符可以变为 acbbca,也可以添加3个变为 abbcbba。方案1只需要添加2个字符,是所有方案中添加字符数量最少的。
输入
输入一个字符串Str,Str的长度 <= 1000。
输出
输出最少添加多少个字符可以使之变为回文字串。
输入样例
abbc
输出样例
2

题解:

题目的意思很简单,就是给定一个字符串,怎么添加最少的字符个数使之变成回文字符串,只是这么光想是想不出来的。
思路1:LCS
将一个字符串str反转之后得到字符串re_str,那么这两个字符串合到一起一定是回文串,比如
str = abbc,re_str = cbba,合到一起的字符串 = abbc cbba,这样就得到了回文字符串,但是其实并不需要添加这么多个长度,str和re_str可能会有公共子序列,比如这里的bb,那么再多加两个bb是不必要的,只要一个c和a即可,所以只要求出str的翻转字符串re_str,然后求两个字符串的LCS(最长公共子序列)即可,最后要添加的字符个数就是str.length() - LCS(str,re_str)
其实这里博主也不是很理解,也是看的别人的。
思路2:区间DP
(如果对区间DP不理解的同学可以去先百度下,其实我用的也很少,但是很好理解)
状态转移方程: 具体去看代码吧,代码中说的比较详细了。

if(str[i] == str[j])
{
	dp[i][j] = dp[i+1][j-1];// 若第i个 == 第j个,说明这一对没问题,只要看里面的i+1 --- j-1           
	if(len == 2)
   	{
     	dp[i][j] = 0;// 长度为2的时候,上面的式子dp[i+1][j-1]是没有意义的,长度为2而且两个字符也相等,那添加的字符个数就为0
   	}
}
else
    {
        // 如果不相等,则选择i+1 --- j 和 i --- j-1的要添加字符串个数小的那一个,并且因为这里有一个字符不匹配,所以要+1,用来匹配第i个或者第j个
        dp[i][j] = min(dp[i+1][j],dp[i][j-1]) + 1;
    }
}

代码1:

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

typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll  INF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double E = exp(1.0);
const int MOD = 1e9+7;
const int MAX = 1e3+5;
string str;

/* LCS解法 */
int dp[MAX][MAX];
int LCS(string str,string tmp)
{
    int len_str = str.length();
    int len_tmp = tmp.length();
    for(int i = 1; i <= len_str; i++)
    {
        for(int j = 1; j <= len_tmp; j++)
        {
            if(str[i-1] == tmp[j-1])
            {
                dp[i][j] = dp[i-1][j-1] + 1;
            }
            else
            {
                dp[i][j] = max(dp[i][j-1],dp[i-1][j]);
            }
        }
    }
    return dp[len_str][len_tmp];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    while(cin >> str)
    {
    	/* LCS解法 */
        string tmp = str;
        reverse(str.begin(),str.end());
        cout << str.length() - LCS(tmp,str) << endl;

    }

    return 0;
}

代码2:

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

typedef long long ll;
const int inf = 0x3f3f3f3f;
const ll  INF = 0x3f3f3f3f3f3f3f3f;
const double PI = acos(-1.0);
const double E = exp(1.0);
const int MOD = 1e9+7;
const int MAX = 1e3+5;
string str;

/* 区间DP解法 */
int DP(string str)
{
    int dp[MAX][MAX];// dp[i][j]表示从字符串的i下标位置 --- j下标位置,形成一个回文串需要添加多少字符
    memset(dp,0,sizeof(dp));

    int str_len = str.length();
    // 字符串长度为0和1的一定都是回文串,所以不需要添加字符,就是默认的0
    for(int len = 2; len <= str_len; len++)// 字符串长度从2开始
    {
        for(int i = 0; i + len - 1 < str_len; i++)// 字符串的起点i
        {
            int j = i + len - 1;// 字符串的终点j
            if(str[i] == str[j])
            {
                dp[i][j] = dp[i+1][j-1];// 若第i个 == 第j个,说明这一对没问题,只要看里面的i+1 --- j-1           }
                if(len == 2)
                {
                    dp[i][j] = 0;// 长度为2的时候,上面的式子dp[i+1][j-1]是没有意义的,长度为2而且两个字符也相等,那添加的字符个数就为0
                }
            }
            else
            {
                // 如果不相等,则选择i+1 --- j 和 i --- j-1的要添加字符串个数小的那一个,并且因为这里有一个字符不匹配,所以要+1,用来匹配第i个或者第j个
                dp[i][j] = min(dp[i+1][j],dp[i][j-1]) + 1;
            }
        }
    }
    return dp[0][str_len - 1];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    while(cin >> str)
    {
        /* 区间DP */
        cout << DP(str) << endl;
    }

    return 0;
}

你可能感兴趣的:(#,51Nod,51Nod算法题解,区间DP,LCS)