poj 3267 The Cow Lexicon E[j]=opt{D+w(i,j)}

题目链接:https://vjudge.net/problem/POJ-3267

题目大意:给出一个长度为L的主字符串 给出n个子字符串  问在主字符串中最少删去几个字母 使主字符串全部由子字符串组成

ps:本题所求主字符串由多个子字符串组成 而最长公共子序列是一个子字符串


解题思路:一开始的思路是套LCS 求出所有子串匹配在主串中要删多少个字母  但推不出关于位置的动态方程

然后就想xjbb  枚举所有字符串然后复杂度是O(n!)哈哈哈哈哈放弃

然后陷入沉思一晚上搭进去了 只能想到用dp[i]记录i-L或0-i之间删去的字母数量 然后睡觉了

想了一天 最后找到了dp[i] = dp[i+1] + 1这个最基本的动态方程 表示i这个位置的字母要被删去 这是最差的结果

以及dp[i] = min(dp[i] , dp[flag] + flag - i - len) 这个方程(可以看下面代码解释)

i表示在这个位置匹配到了一个子字符串的首字母与主串中i位置的字母相同

flag表示这个子字符串匹配完毕后在主串中的位置

扫描二维码关注公众号,回复: 2358166 查看本文章

len表示子字符串长度

flag - i - len 表示这个子字符串与主串匹配在主串中需要删去的字母数量

dp[flag]表示从flag - L这个区间已经删去的字母数量 

按照这个基本思路我们就可以开怼了 因为我们的状态方程是dp[i] = dp[i+1] + 1 所以我们要从后往前扫描 

这个破结构体可以用 string str[600]代替 写的时候我脑袋抽了

#include <iostream>
#include <string.h>
#include <string>
#include <algorithm>
using namespace std;
typedef struct ss
{
    char word[400];
};
int dp[400];//最好清空一下
int main() {
    char str[400];
    ss a[610];
    int n,L;
    cin >> n >> L;
    cin >> str;
    for(int i = 0;i<n;i++)
        cin >> a[i].word;//n个子字符串
    dp[L] = 0;//L到L这个位置要删去0个字母 初始化
    for(int i = L - 1;i >= 0; i--)//扫描主串
    {
        dp[i] = dp[i+1] + 1;//默认为最差结果 该处字母要删除 
        int len;
       for(int j = 0;j < n;j++)
       {
           len = strlen(a[j].word);//没好好学c语言 strlen是unsigned int型 一开始在下面的min(int,int)函数中一直报编译错误 
           if(a[j].word[0] == str[i] && strlen(a[j].word) <= L - i)//如果子串首字母与该处字母相同 开始匹配
           {
               int s = 0;
               int flag = i;
               while(s<strlen(a[j].word))//匹配直到子串被完全匹配
               {
                   if(a[j].word[s]==str[flag])
                   {
                       flag++;
                       s++;
                   }
                   else
                   {
                       flag++;
                   }
                   if(flag == L)
                       break;
               }
               if(s == strlen(a[j].word))//如果子串被成功匹配
               {
                   dp[i] = min(dp[i],dp[flag] + flag - i - (int)strlen(a[j].word));//要加int强制转换 或者用len
               }                   //flag-L的之间被删去的字母数加上本次匹配需要删去的字母数就是在i这个位置需要删去的最小字母数量     
           }                       
       }
    }
    cout<<dp[0]<<endl;//输出0-L之间删去的字母数量
    //std::cout << "Hello, World!" << std::endl;
    return 0;
}
dp大法好


猜你喜欢

转载自blog.csdn.net/zhengyuan233/article/details/66973849