【BZOJ3214】[ZJOI2013] 丽洁体(毒瘤字符串读入+动态规划水题)

点此看题面

大致题意: 给你一个长句(s)以及三个短句(a,b,c),求至少删去(s)中多少个单词,才能使得到的字符串以(a)为开头,以(c)为结尾,且中间部分还含有一个(b)

前言

我觉得我做了假的(ZJOI)(ZJOI)怎么可能这么水。。。

这是一个我都能推出来的(DP),只可惜挂在了读入。。。

毒瘤字符串读入

这道题读入字符串读得我真是想一口@&*&&¥@#【某陈年**】。

主要是判回车这个地方特别恶心,而且按照我原本的字符串统计方式,在本地是没有问题的,结果一交总会莫名其妙多出一个字符串(后来发现是由于题库上行末是" "两个字符)。

动态规划水题

考虑开头和结尾显然贪心,能选就选。

主要是中间部分,我们可以设(f_x)表示匹配到第(x)个单词所需删除的最少单词数。

那么假设第(i)个单词可以匹配到(x),第(j)个单词可以匹配到(x-1)(j<i)),则就有转移方程:

[f_x=f_{x-1}+i-j-1 ]

然后我们整理一下,发现如果设(g_{x-1})为最小的(f_{x-1}-j-1),就可以得到转移方程:

[f_x=i+g_{x-1} ]

(g_x)显然可以在(DP)同时,计算完(f_x)后直接更新。

然后注意一下每个单词只(DP)自己能匹配到的(x),否则可能会被卡。

代码

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define Gmin(x,y) (x>(y)&&(x=(y)))
using namespace std;
int m,g[N+5];vector<int> V[N+5];map<string,int> S;
struct Sentence {int n,v[N+5];}s,a,b,c;
I void read(Sentence& x)//读入一个句子
{
	RI i,l;char c;string ss="";W(true)
	{
		if(c=getchar(),c>='a'&&c<='z') ss+=c;//维护单词
		else if(ss.length()) !S[ss]&&(S[ss]=++m),x.v[++x.n]=S[ss],ss="";//读完了一个单词
		if(c=='
') return;//读到回车退掉
	}
}
int main()
{
	RI i,j,sz,l,r,p,res=1e9,ans=0;read(s),read(a),read(b),read(c);
	for(p=l=1;p<=a.n;++l) s.v[l]==a.v[p]?++p:++ans;//开头
	for(p=c.n,r=s.n;p;--r) s.v[r]==c.v[p]?--p:++ans;//结尾
	for(i=1;i<=b.n;++i) g[i]=1e9,V[b.v[i]].push_back(i);//初始化
	for(i=l;i<=r;++i) for(sz=V[s.v[i]].size(),j=sz-1;~j;--j)//只DP能匹配到的单词
		p=V[s.v[i]][j]^1?i+g[V[s.v[i]][j]-1]:0,Gmin(g[V[s.v[i]][j]],p-i-1),//计算f,然后更新g
		V[s.v[i]][j]==b.n&&Gmin(res,p);//统计答案
	return printf("%d",ans+res),0;//输出答案
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ3214.html