POJ #3267 The Cow Lexicon 型如" E[j] = opt{D+w(i,j)} "的简单DP 区间DP

Description



  问题的描述以及输入输出的样例可以看这里:链接

思路



  虽然 DISCUSS 中总有人说水题,但是我觉得这道题的质量可以 (或许我比较弱ORZ ,在做过的 DP 题里算 medium 难度。

  题目的意思是给你一个主串和一堆子串,需要你将子串和主串完全匹配上,在匹配过程中可以删除主串中匹配不上的字符,最后统计出被删除的最少字符数目。

  比如主串是 carmsr ,子串有 car 、mr 两种。可以只用 car 去匹配,那么匹配不上的字符有 m、s、r 三个,所以需要删除三个字符;可以用 car、mr 去匹配,那么匹配不上的字符有 s 一个,所以需要删除一个字符。那么在个样例里最终的输出就是 1。

  枚举算法的时间复杂度是指数阶,直接出局,决定从后往前简单搜索。在过程中发现这道题是具有最优子结构的,即问题的最优解是由子问题的最优解演变而来的,当前位置最少删除字符数目与之前位置的最少删除字符数目有直接关系,即当前位置没有被匹配上的话,那么当前位置的最少删除字符数目就是前一个位置的最少删除字符数目加一;如果匹配上的话,演变关系比较饶一点,还是通过例子说明吧:

//主串 browndcodw,子串 cow

browndcodw
      co  w
      l   r     

//当前位置位于末尾的 w ,子串 cow 匹配到的位置如图所示//那么,当前位置的删除字符数目 = r - l +1 - 3 + 前6个字符的最小删除字符数目 

  通过这个例子还可以发现,原问题还有很多公共的子问题,若此时再给一个子串 cod 进行匹配,那么两个子串匹配后主串中剩下的子串的长度是一致的,它们的子子问题其实是同一个问题。

  我们定原问题为状态,即状态为前 i 个字符需要删除的字符数,设为 dp[i] ,那么可以推出状态转移方程:

    

  有了方程,写算法就容易多了。算法使用采用迭代实现,时间复杂度为 O(W·L^2)   

#include<iostream>
#include<vector>
#include<string>
using namespace std;
#define INT_MAX 600000
const int MAX_WORD_NUM = 600;
const int MAX_MES_LENGTH = 300;
int dp[MAX_MES_LENGTH + 1];
int len[MAX_WORD_NUM + 1];
string dir[MAX_WORD_NUM + 1];

int main(void) {
    int word_num, mes_length;
    string mes;
    cin >> word_num >> mes_length;
    cin >> mes;
    //int dp[mes_length+1];
    //int len[word_num+1];
    dp[0] = 0;
    for (int i = 1; i <= mes_length; ++i) {
        dp[i] = INT_MAX;
    }
    //string dir[word_num+1];
    for (int i = 1; i <= word_num; i++) {
        cin >> dir[i];
        len[i] = dir[i].size();
    }

    for (int i = 1; i <= mes_length; ++i) {
        bool match_flag = false;
        for (int k = 1; k <= word_num; k++) {
            int l = i-1, r = i-1;
            //从后往前匹配
            int j;
            for (j = len[k]-1; l >= 0; ) {
                if (dir[k][j] == mes[l]) {
                    j--;
                    if (j == -1) //单词匹配成功,l无需再左移
                        break;
                }
                else if (dir[k][j] != mes[l] && j == len[k]-1) {
                    r--;
                }
                l--;
            }
            //*cout << "现在是第 " << k << "个单词在进行匹配! " <<"l is :" << l << " " << "r is :" << r << endl;

            //主串匹配上了当前单词
            if (j == -1) {
                //dp为含前i个字符的主串需要删去的字符总数
                dp[i] = std::min(dp[i], dp[l] + i - l - len[k] ); //dp[l] + (i-1) - l - len[k]
                match_flag = true;
            }
        }
        //mes的前i个字符都没有匹配上任何单词
        if (match_flag == false) {
            dp[i] = dp[i-1] + 1;
        }
    }
    cout << dp[mes_length] << endl;
    return 0;
}

  

原文地址:https://www.cnblogs.com/Bw98blogs/p/8419309.html

时间: 2024-11-06 09:50:39

POJ #3267 The Cow Lexicon 型如" E[j] = opt{D+w(i,j)} "的简单DP 区间DP的相关文章

poj 3267 The Cow Lexicon (dp)

链接:poj 3267 题意:给定一个主串,和单词序列,问最少在主串删除多少字母, 可以使其匹配到单词序列,如 browndcodw cow milk white black brown farmer 删除主串中的两个d,brown和cow就与整个主串匹配了 分析:dp[i]表示从主串中第i个字符开始,到第L个字符(结尾处) 这段区间最少要删除的字符数, 则状态转移方程为: dp[i]=dp[i+1]+1  不能匹配时 dp[i]=min(dp[i],dp[pos]+pos-i-m)  可以匹配

poj 3267 The Cow Lexicon 动态规划

题目链接:http://poj.org/problem?id=3267 给一个字典 求原串中至少去掉多少个字母可以让已给字典可以完整覆盖原串 在跟字典作匹配时 注意原串是“可跳跃的” 即存在“删掉某个字母后 该字母的前后两部分拼起来组成单词” 针对这种情况 考虑使用两个指针 匹配时:同时往后滑1格 不匹配时:仅指向原串的指针往后滑1格 之所以网上大部分题解都是从原串的最后往前推 是因为这样写起来下标容易控制 最外层循环的原串从后往前 则匹配过程可以较自然地从前往后 而匹配过程明显更为复杂 所以这

POJ 3267 The Cow Lexicon

Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 10021   Accepted: 4814 Description Few know that the cows have their own dictionary with W (1 ≤ W ≤ 600) words, each containing no more 25 of the characters 'a'..'z'. Their cowmunication sy

POJ 3276 The Cow Lexicon DP-字符串匹配

点击打开链接 The Cow Lexicon Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8325   Accepted: 3934 Description Few know that the cows have their own dictionary with W (1 ≤ W ≤ 600) words, each containing no more 25 of the characters 'a'..'z'.

Treats for the Cows POJ - 3186 dp 区间dp

//dp[i][j]表示第i次从左边取,第j次从右边取的价值,所以我们可以得到状态方程 //dp[i][j]=max(dp[i-1][j]+(i+j)*a[i],dp[i][j-1]+(i+j)*a[n-j+1]) (i > 0 && j > 0 ) //dp[i][0]=dp[i-1][0]+i*a[i],dp[0][i] dp[0][i-1]+i*a[n-i+1]; #include<cstdio> #include<cmath> #include&

uva1626 poj 1141 Brackets Sequence 区间dp 打印路径

// poj 1141 Brackets Sequence // 也是在紫书上看的一题,uva就是多了一个t组数据. // 经典区间dp // dp(i,j)表示区间[i,j]内所需要增加的括号数目 // 则分为两种情况 // 一种是s[i]和s[j]是匹配的则 // dp[i][j] = min(dp[i][j],dp[i+1][j-1]) // 另外一种情况是不匹配 // dp[i][j] = min(dp[i][j],dp[i][k]+dp[k+1][j]){i<k<j}; // 但是无

POJ 1141 Brackets Sequence (区间dp 记录路径)

题目大意: 给出一种不合法的括号序列,要求构造出一种合法的序列,使得填充的括号最少. 思路分析: 如果只要求输出最少的匹配括号的数量,那么就是简单的区间dp dp[i][j]表示 i - j 之间已经合法了最少添加的括号数. 转移 就是 dp[i] [j] = min  (dp[i+1][j]+1 , dp[ i+ 1] [ k -1 ] + dp[k+1] [j] (i k 位置的括号匹配)) 其次我们要记录路径,你发现  如果 dp [i] [j] 是由 dp [i+1] [j] 转移过来的

POJ 3280 Cheapest Palindrome(区间DP求改成回文串的最小花费)

题目链接:http://poj.org/problem?id=3280 题目大意:给你一个字符串,你可以删除或者增加任意字符,对应有相应的花费,让你通过这些操作使得字符串变为回文串,求最小花费.解题思路:比较简单的区间DP,令dp[i][j]表示使[i,j]回文的最小花费.则得到状态转移方程: dp[i][j]=min(dp[i][j],min(add[str[i]-'a'],del[str[i]-'a'])+dp[i+1][j]); dp[i][j]=min(dp[i][j],min(add[

【POJ 3267】 The Cow Lexicon

[POJ 3267] The Cow Lexicon 训练计划里把这题排到了topo里....然后我就这么死盯研究了一周topo算法(期间经历了三个人生风波....大物考试 高数考试跟模电考试----)啥不说了--上dp代码----没错 这是个dp!...赤果果的dp..就呢么傻呆呆地研究Topo算法--结果没研究出来.. 题意是给一个字符串和m个单词组成的字典,问最少删除几个字母能让这个字符串变成由字典中几个单词首位链接组成的字符串 dp思路还算好想 逆推 dp数组的下标是遍历字符串的起点 然