nyoj 37 nyoj15区间型动态规划

  最近几天在做区间型动态规划,刚开始一碰到字符串的问题就头疼,就会潜意识的跳过。真的静下心来做做 你会发现其实没你想的那么难。

   一般经典的问题如:合并石子,回文字符串等。今天就说一下 回文字符串 括号匹配。


(1)回文字符串(nyoj 37)

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

现将串倒置(如题中:db3bA),再求两个串(Ab3bd,db3bA)的最长公共子序列,可以看成是串本身存在最长的子回文序列,也就是不需要添加的,再用总长度减去最长公共子序列就可以得到最少需要添加的字符数


方法二:
这个思想和石子合并有点像。既然是区间型动态规划,我们就很容易想到先从小区间开始求,得到当前小区间i~j的最优解,然后再由小区间往外拓展求得更大的区间m~n(m<=i ,n>=j),最后就会得到整个串的最优解。
      d[i][j] = d[i+1][j-1]                                  (s[i] = s[j])
d[i][j] = min(d[i+1][j] + 1, d[i][j-1] + 1)    (s[i] != s[j])

      当s[i] = s[j]时,i位与j位相等不需要添加,直接等于d[i+1][j-1] 就行,当s[i] != s[j]   时,需要添加i或j位,选择花费最少的,即 min(d[i+1][j] + 1, d[i][j-1] + 1) 。



注:在我们求各个状态(区间)时应该先从小区间开始(求区间间隔为1时,再求区间间隔为2时......),不断扩大,最后求得整个区间。


     

     


(2)括号匹配(nyoj15)

描述
给你一个字符串,里面只包含"(",")","[","]"四种符号,请问你需要至少添加多少个括号才能使这些括号匹配起来。
如:
[]是匹配的
([])[]是匹配的
((]是不匹配的
([)]是不匹配的
输入
第一行输入一个正整数N,表示测试数据组数(N<=10)
每组测试数据都只有一行,是一个字符串S,S中只包含以上所说的四种字符,S的长度不超过100
输出
对于每组测试数据都输出一个正整数,表示最少需要添加的括号的数量。每组测试输出占一行
样例输入
4
[]
([])[]
((]
([)]
样例输出
0
0
3
2
这个题和上面的题思路差不多, 不过还是有很多细节需要注意。

代码:
#include<iostream>
#include<cstdio>
#include<string.h>
#include<math.h>

using namespace std;

int len, d[110][110];
char s[110];

void dp()
{
    for(int i = 1; i <= len-1; i++)
    {
        for(int j = 0; j < len-i; j++)
        {
            if((s[j]== '[' && s[j+i]==']') || (s[j]=='(' && s[j+i] == ')'))
            {
                if(i == 1)
                    d[j][i+j] = 0;   //当j位与j+1位(相邻位)匹配时,不需要任何添加
                else
                    d[j][j+i] = d[j+1][j+i-1];    //当j位于j+i位相匹配(但是不相邻),这时就需要由子区间(j+1,i+j-1)求得
            }
            else
                d[j][j+i] = min(d[j+1][j+i]+1,d[j][i+j-1]+1);

//注意:这块非常重要,这不仅需要遍历每种区间(d[j][j+i])的情况,而且需要求一下这种区间的最优解,即d[j][j+i]=min(d[j][j+i],d[j][k]+d[k+1][j+i])
            for(int k = 0; k < j+i; k++)
                d[j][j+i] = min(d[j][j+i],d[j][k]+d[k+1][j+i]);
        }
    }
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        scanf("%s", &s);
        len = strlen(s);
        for(int i = 0; i <= len; i++)
        {
            for(int j = 0; j <= len; j++)
            {
                if(i == j)
                    d[i][j] = 1;    //这也需要注意,不想回文单独的一位可以构成回文,这是单独的一个括号的一半需要有另一半匹配
                else
                    d[i][j] = 10e8;
            }
        }
        dp();
        printf("%d\n", d[0][len-1]);
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值