题目链接: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表示这个子字符串匹配完毕后在主串中的位置
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大法好