dp问题 添加最少的字符构成回文串

本文介绍了一种算法,用于确定将任意字符串转换为回文串所需的最少字符插入数量。通过寻找原始字符串及其反转字符串之间的最长公共子串来实现。
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

思路:将原串倒置得到现串,找到两个串的最长公共子串的长度,长度n-找到的公共长度就是需要添加的字符数

     先贴如一个何求得两个穿的最长公共子串的代码
     
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
string a,b;
int dp[1000][1000];
int main()
{
	int num,i,j,l1,l2,maxn;
	while(cin>>a>>b)
	{
	  l1=a.size();
	  l2=b.size();
	  memset(dp,0,sizeof(dp));
	  
	  for(int i=0;i<l1;i++) 
	  for(int j=0;j<l2;j++)
	  {
	  	if(a[i]==b[j]) dp[i+1][j+1]=dp[i][j]+1;
	  	else dp[i+1][j+1]=max(dp[i+1][j],dp[i][j+1]);
	  }
	  cout<<dp[l1][l2]<<endl;
	  
	}
	return 0;
}

在这个问题中如果定义dp[5005][5005]肯定会内存超限
所以用滚动数组解决,其实这个题中每次用到的dp行数是当前行和前一行 所以我们可以只用dp[2][5005]就可以啦

代码如下:只是在上一个程序中改动一下即可
#include <stdio.h>  
#include <string.h>   
#include <iostream>  
#include <algorithm>   
using namespace std;  
char str1[5010];  
char str2[5010];  
int dp[2][5010];     //滚动数组 解决内存超限问题 
int main()  
{  
    int n,i,j;  
    while(~scanf("%d",&n)){  
        getchar();  
        for(i=1;i<=n;i++)
		{  
            scanf("%c",&str1[i]);  
            str2[n-i+1]=str1[i];  
        }  
        memset(dp,0,sizeof(dp));  
        for(i=1;i<=n;i++)  
        for(j=1;j<=n;j++){  
          if(str1[i]==str2[j])  
                dp[i%2][j]=dp[(i-1)%2][j-1]+1;  
            else  
                dp[i%2][j]=max(dp[(i-1)%2][j],dp[i%2][j-1]);  
        }  
        printf("%d\n",n-dp[n%2][n]);  
    }  
    return 0;  
}  


### 区间动态规划实现字符串转换回文串最少操作次数算法 #### 算法描述 为了求解将给定字符串 `s` 转换回文串所需要的最小插入次数,采用区间动态规划方法是一种有效策略。此方法的核心在于通过构建二维数组 `dp` 来记录不同子区间的最优解。 对于任意一对索引 `(i, j)`,其中 `i ≤ j`: - 如果 `s[i] == s[j]`,那么无需额外插入字符来匹配这对边界字符;因此只需关注去掉这两端后的子串 `[i+1:j-1]` 所需的操作数即可。 - 若 `s[i] != s[j]`,则至少需要一次插入使得两端相等。可以选择在左侧或右侧增加相应的字符,取两者所需总步数较小者作为最终结果。 具体来说,状态转移方程如下所示[^2]: \[ \text{if } s[i] = s[j]:\quad dp[i][j] = dp[i + 1][j - 1]\] \[ \text{else}:\quad dp[i][j] = \min(dp[i + 1][j], dp[i][j - 1]) + 1 \] 初始条件设定为当子串长度小于等于一时(即单个字符本身已是回文),其对应的 `dp` 值设为零。 最后所求的结果就是整个输入字符串范围内的 `dp[0][n-1]`,这里假设原字符串长度为 `n`。 下面是基于上述逻辑的具体 C++ 实现代码示例: ```cpp class Solution { public: int minInsertions(string s) { const int n = s.length(); vector<vector<int>> dp(n, vector<int>(n)); // Fill the DP table from shorter substrings to longer ones. for (int length = 2; length <= n; ++length){ for (int start = 0; start <= n - length; ++start){ int end = start + length - 1; if (s[start] == s[end]){ dp[start][end] = dp[start + 1][end - 1]; } else{ dp[start][end] = min(dp[start + 1][end], dp[start][end - 1]) + 1; } } } return dp[0][n - 1]; } }; ``` 该程序首先创建了一个大小为 `n×n` 的二维向量 `dp` 并将其初始化为全零矩阵。接着按照子串长度从小到大遍历所有可能的子串组合,并依据之前提到的状态转移规则更新对应位置上的值。最终返回的是最外层循环结束后位于左上角的位置处存储的数据——即将原始字符串完全转化为回文形式所需要执行的最少插入动作数量[^4].
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值