区间dp-添加最少字符成为回文串

本文介绍了一种通过记忆化搜索解决回文字符串问题的方法。该方法利用区间动态规划技术,针对给定字符串寻找最少需要添加多少字符才能使其变成回文字符串。

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

水题吧,记忆化搜索即可


回文字符串

时间限制: 3000 ms  |  内存限制: 65535 KB
难度: 4
描述
所谓回文字符串,就是一个字符串,从左到右读和从右到左读是完全一样的,比如"aba"。当然,我们给你的问题不会再简单到判断一个字符串是不是回文字符串。现在要求你,给你一个字符串,可在任意位置添加字符,最少再添加几个字符,可以使这个字符串成为回文字符串。
输入
第一行给出整数N(0<N<100)
接下来的N行,每行一个字符串,每个字符串长度不超过1000.
输出
每行输出所需添加的最少字符数
样例输入
1
Ab3bd
样例输出
2
来源
IOI 2000

/****************************************
* author:crazy_石头
* pro:回文字符串-区间dp 
* date:2014/04/16
* algorithm: dp 
* 思路: 区间dp固定模式:
*  考虑i~j区间的字符串;
* 1.是一个字符的话回文,添加字符数量为0;
* 2.是两个字符并且相等的话也是回文的,添加字符数量为0;
* 3.一般情况,就是考虑第i个字符,如果第j个字符处的字符与它不相等的话,
*那么把第i个字符和后面的串隔离开,给第i个字符单独添加一个字符,即:
* dp[i][j]=dp[i+1][j]+1;(dp[i+1][j]是隔离开的串,加的1就是给dii个字符加的
* 所以dp[i][j]=std::min(dfs(i+1,j)+1,dfs(i,j-1)+1);看懂了吧? 
*****************************************/ 
#include 
  
   
#include 
   
    
#include 
    
     
#include 
     
      
#include 
      
       
#include 
       
         #include 
        
          #include 
         
           #include 
          
            using namespace std; #define INF INT_MAX #define eps 1e-8 #define A system("pause") #define rep(i,h,n) for(int i=(h);i<=(n);i++) #define ms(a,b) memset((a),(b),sizeof(a)) #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define mod 1e9+7 #define LL long long const int maxn=1000+5; char ch[maxn]; int d[maxn][maxn]; inline int dfs(int i,int j) { if(~d[i][j]) return d[i][j]; if(i==j) return d[i][j]=0; if(ch[i]==ch[j]&&i+1==j)//是回文双字符,无需添加字符 return d[i][j]=0; if(ch[i]==ch[j]) return d[i][j]=dfs(i+1,j-1);//匹配的话不管最外层括号,相当于往近了一层; d[i][j]=std::min(dfs(i+1,j)+1,dfs(i,j-1)+1); return d[i][j]; } int main() { int n; scanf("%d",&n); while(n--) { scanf("%s",ch); ms(d,-1); int len=strlen(ch); int ans=dfs(0,len-1); printf("%d\n",ans); } 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].
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值