题man戳我
题目描述
给定 n 和 n 个信息,每个信息包含一个词性 a (只有三种:名,动,辅)和对应的词 mot ,形为为“ a.mot ”。(一次可能多词性)
最后给一个长度不大于5KB的冰峰文文章,将这篇冰峰文文章划分为最少的句子,在这个前提下,将文章划分为最少的单词时,求划分的句子数量和单词数量。
划分标准:
(别问我为什么盗图。。
1≤n≤ mot.len≤20
solution
首先要搞懂题目中的图是什么玩意(我真的看了好久都没看懂。。)
所有语法简述下来就是:
1.名词短语是许多个辅词加一个名词组成的。
2.动词短语是许多个辅词加一个动词组成的。
3.一个句子以名词短语开头,名词短语和动词短语交替出现而组成的。
4.文章为多句话组成。
所以对于任意词,有四种类型:
1.名词。
2.动词。
3.辅名词的辅词。
4.辅动词的辅词。
状态应该很自然了。。(要什么设什么呗)
f [ j ] [ i ] [ 0 ] 指前 i 个字母,最后一个单词是名词,构成了 j 个句子的最小单词数。
f [ j ] [ i ] [ 1 ] 指前 i 个字母,最后一个单词是动词,构成了 j 个句子的最小单词数。
f [ j ] [ i ] [ 2 ] 指前 i 个字母,最后一个单词是辅词,后面要接动词,构成了 j 个句子的最小单词数。
f [ j ] [i ] [ 3 ] 指前 i 个字母,最后一个单词是辅词,后面要接名词,构成了 j 个句子的最小单词数。
状态转移方程就按照语法看能否转移就行
f [ j ] [ i ] [ 0 ] ⟹ min ( f [ j ] [ k ] [ 1/3 ] , f [ j−1 ] [ k ] [ 0/2 ] )
f [ j ] [ i ] [ 1 ] ⟹ min ( f [ j ] [ k ] [ 0 / 2 ] )
f [ j ] [ i ] [ 2 ] ⟹ min ( f [ j ] [ k ] [ 0 / 2 ] )
f [ j ] [ i ] [ 3 ] ⟹ min ( f [ j ] [ k ] [ 1/3 ] , f [ j − 1 ] [ k ] [ 0/1 ] )
实际上看式子的话, j 那一维可以滚动起来。(虽然不滚掉好像问题不大,但省空间多好。。)
最后答案就是按题目来,求一个最小的ans ,存在 [ 0/1 ] f [ ans ] [ len] [ 0/1 ] ,如果都存在,取较小值。
DP 这里就结束了,考虑如何实现。
明显 DP 的复杂度不允许我们每次枚举所有单词再去比较。
所以想到了用一个比较实用的东西 trie 可以把速度拉起来。
基本上这题就搞定了,就是注意一定把数组开稍微大点(我因为忽略数组大小而傻乎乎地去调了半个小时程序了)
code
#include<bits/stdc++.h>
#define reg register
using namespace std;
typedef long long ll;
const int N=1e3+10,M=6e3+10,K=3e4+10;
const int INF=0x3f3f3f3f;
int n,m,mlth,f[2][M][4],tri[M][24],tot,op,ans1,ans2;
char sw[M],sd[M];
struct trie{
int tr[26],opt,it;
inline void clear(){
memset(tr,0,sizeof(tr));
it=-1;opt=0;
}
}trie[K];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline void insert(char s[],int lth,int opt){
int id=0,val;
for(int i=2;i<lth;++i){
val=s[i]-'a';
if(!trie[id].tr[val]){
trie[++tot].clear();
trie[tot].it=val;
trie[id].tr[val]=tot;
}
id=trie[id].tr[val];
}
trie[id].opt|=opt;
}
inline int find(int lt,int rt){
int id=0,val;
for(int i=lt;i<=rt;++i){
val=sd[i]-'a';
if(!trie[id].tr[val])return 0;
id=trie[id].tr[val];
}
return trie[id].opt;
}
inline void mian(){
ans1=0;ans2=INF;
trie[0].clear();
memset(tri,-1,sizeof(tri));
memset(f,INF,sizeof(f));
for(int i=1;i<=n;++i){
scanf("%s",sw);m=strlen(sw);
mlth=max(m,mlth);
if(sw[0]=='n')insert(sw,m,1);
else if(sw[0]=='v')insert(sw,m,2);
else if(sw[0]=='a')insert(sw,m,4);
}
scanf("%s",sd+1);m=strlen(sd+1)-1;
f[0][0][0]=0;
for(int lin=1;lin<=m;++lin){
int now=op^1,pre=op;
for(int i=1;i<=m;++i){
memset(f[now][i],INF,sizeof(f[now][i]));
int lim=max(i-mlth,0);
for(int j=i-1;j>=lim;--j){
if(tri[j+1][i-j]==-1)
tri[j+1][i-j]=find(j+1,i);
int opti=tri[j+1][i-j],nowi,prei;
if(opti&1){
nowi=min(f[now][j][1],f[now][j][3]);
prei=min(f[pre][j][0],f[pre][j][2]);
f[now][i][0]=min(f[now][i][0],nowi+1);
f[now][i][0]=min(f[now][i][0],prei+1);
}//不能有else
if(opti&2){
nowi=min(f[now][j][0],f[now][j][2]);
f[now][i][1]=min(f[now][i][1],nowi+1);
}//不能有else
if(opti&4){
nowi=min(f[now][j][0],f[now][j][2]);
f[now][i][2]=min(f[now][i][2],nowi+1);
nowi=min(f[now][j][1],f[now][j][3]);
prei=min(f[pre][j][0],f[pre][j][1]);
f[now][i][3]=min(f[now][i][3],nowi+1);
f[now][i][3]=min(f[now][i][3],prei+1);
}//不能有else
}
}
ans2=min(f[now][m][0],f[now][m][1]);
if(ans2!=INF){ans1=lin;break;}
op^=1;
}
printf("%d\n%d\n",ans1,ans2);
}
int main(){
n=read();
mian();
return 0;
}
(抄者自罚给我一键三连)