动态规划:字符串接龙问题

刚刚接触 dp,仅仅以初学者的视角给出一点见解

动态规划

1. 基本思想

动态规划的基本思想是:将一个大问题拆分成几个小问题,通过分别求解这几个小问题来解决最终的问题。

2. 主要特征

动态规划(Dynamic Programming) 思想来思考问题主要考虑两个方面,一个是状态表示,需要明确 dp[i][j]的含义(同时也要考虑问题需要几个维度来表示,这边的示例为二维)。对于dp[i][j],需要从两个方面进行考虑:

状态表示dp[i][j]是一种状态的集合,如01背包问题,那就代表了仅考虑前i件物品且总体积小于等于背包体积的所有选法。其中dp[i][j]的值代表这个集合中的一种属性,可以是所有元素中的最大值,最小值等等。

状态计算(状态转移方程):这边就需要对当前问题集合进行划分,划分成几个小问题,同时要考虑如何从这些小问题中得出当前这个问题的答案,也就是所谓的状态转移方程,如01背包问题中的状态转移方程就是f[i][j] = max(f[i - 1][j], f[i - 1][j - v[i]] + w[i]),也就是分为了两个子问题:在前i个物品中,选择或不选择第i件物品的情况。

问题描述

在这里插入图片描述
在这道题目中,也考虑使用动态规划的思想来考虑问题。删掉最少的字符串可以转而去求最长接龙字符串即可。那么要求解规模为n的最长接龙字符串能否分解成各个小问题呢?我的想法是用dp[i][j]表示前i 个字符串且以j 为结尾的接龙字符串最大长度(Amazing)这个表示方式是我认为最妙也是最关键的地方。这边的j对应小写字母a ~ z映射到0 ~ 25的情况。在这样的状态表示下,如何进行子问题的划分呢?类似的,分为在前i - 1个字符串组合基础上考虑是否接入第i个字符串。当然在这道题中会发现,仅仅当首字母和上一个字符串最后一个字母相同的时候可以接入这个字符串,对于不同的情况其最大长度与i - 1个字符串相同结尾的最大长度是相同的。

这边还有一个小问题,我第一次想的时候没有考虑到,接入第i个字符串的情况下,既需要考虑dp[i - 1][st] + 1也需要考虑dp[i - 1][ed]i - 1规模下,是在以这个字符串开头字母结尾的接龙句子中接入这个字符串长度大还是原来就是以这个字符串最后一个字母为结尾的长度更大。(后一种情况容易遗漏)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;

const int N = 100010;

int dp[N][26];  // dp数组含义是前 i 个字符串且以 j 为结尾的接龙字符串最大长度
string strings[N];

int main()
{
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> strings[i];

    dp[1][strings[1][strings[1].size() - 1] - 'a'] = 1;
    for(int i = 2; i <= n; i++) // i = 1 的时候已经处理完成,直接从 2 开始
    {
        int st = strings[i][0] - 'a'; //代表当前字符串的首字母
        int ed = strings[i][strings[i].size() - 1] - 'a'; //代表当前字符串的最后一个字母
        //拆分为两个子情况:一个是接入这个字符串(接入时还需要判断与前一个以ed结尾的哪一个更长,否则就把更优的情况舍掉了) 一个是不接入这个字符串
        dp[i][ed] = max(dp[i - 1][ed], dp[i - 1][st] + 1); //可以连接在 i - 1 个字符串且当前字符串开头字母为结尾的后面 或者是前面以ed为结尾更长的值!
        for (int j = 0; j < 26; j++)
            if(j != ed)
                dp[i][j] = dp[i - 1][j];
    }
    int ans = 0;
    for(int i = 0; i < 26;i++)
        ans = max(ans, dp[n][i]);
    cout << n - ans;
}

感谢问求的助教们辛苦出题~ (debug真的de了很久。。。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值