POJ3267

从今天开始POJ里的一部分类型的题目就一般不放在一起写了

一个是太丑,格式麻烦,第二个是以后的题目难度都有所增大,因此一道题可能就要写蛮长

尤其是DP这一块,以前一直没好好学习,现在从基础的先开始吧

题意:给你一个较长的长度为L的串,以及W个长度较短的串。现在问你要从较长串中删去最少几个字符,才能使得这个串由那些长度较短的串组成。

好,题意有点难懂是吧,可以看一下example

删去那两个d使原串变成browncow,既满足要求

首先这个问题肯定满足从局部最优可以推到全局最优

即设f[i]表示先i个字符中最少要删去多少个字符才满足要求

然后发现f[i]可以从它前面转移:

f[i]=min(f[i-1]+1,f[j]+work(s(j,i),t[k]))(1<=i<=l;0<=j<i;1<=k<=w)

其中work表示要对s(j,i)删去几个字符才能使s(j,i)等于t[k]

CODE

#include<iostream>
#include<string>
using namespace std;
const int INF=1e9;
int n,m,cnt,f[305];
string s,t[605];
inline int get(string t,string s)
{
	register int i;
	int p=0;
	for (i=0;i<t.size();++i)
	if (s.find(t[i],p)!=string::npos) p=s.find(t[i],p)+1; else return INF;
	return s.size()-t.size();
}
inline int work(string s)
{
	int tot=INF;
	for (register int i=1;i<=n;++i)
	tot=min(tot,get(t[i],s));
	return tot;
}
inline int min(int a,int b)
{
	return a<b?a:b;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	std::ios::sync_with_stdio(false);
	register int i,j;
	cin>>n>>m>>s;
	for (i=1;i<=n;++i)
	cin>>t[i];
	for (i=1,f[1]=1;i<=m;++i,f[i]=f[i-1]+1)
	for (j=0;j<i;++j)
	{
		string temp=s.substr(j,i-j);
		f[i]=min(f[i],f[j]+work(temp));
	}
	printf("%d",f[m]);
	return 0;
}

但是这样的话时间复杂度爆炸

然后我们很显然地发现,对于一个串,只需要找到在前面第一个包含它的字串然后转移即可,就可以省去枚举j的那一维

CODE

#include<iostream>
#include<string>
using namespace std;
const int INF=1e9;
int n,m,cnt,f[305];
string s,t[605];
inline int min(int a,int b)
{
	return a<b?a:b;
}
int main()
{
	//freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
	std::ios::sync_with_stdio(false);
	register int i,j;
	cin>>n>>m>>s;
	for (i=1;i<=n;++i)
	cin>>t[i];
	for (i=1,f[1]=1;i<=m;++i,f[i]=f[i-1]+1)
	{
		for (j=1;j<=n;++j)
		{
			int p1=i-1,p2=t[j].size()-1;
			while (p2>=0&&p1>=0)
			{
				if (s[p1]==t[j][p2]) --p2; --p1;
			}
			if (p2<0) f[i]=min(f[i],f[p1+1]+i-p1-t[j].size()-1);
		}
	}
	printf("%d",f[m]);
	return 0;
}
原文地址:https://www.cnblogs.com/cjjsb/p/8954879.html