把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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

点此看题面

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

前言

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

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

毒瘤字符串读入

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

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

动态规划水题

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

主要是中间部分,我们可以设\(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=='\n') 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;//输出答案
}
posted @ 2020-05-15 16:08  TheLostWeak  阅读(133)  评论(0编辑  收藏  举报