【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;//输出答案
}
待到再迷茫时回头望,所有脚印会发出光芒