做出这道题关键在于理解语法。其中名词短语和动词短语给出的都是递归定义,可以转化为更直观的,
<名词短语> ::= {<辅词>} <名词>,
<动词短语> ::= {<辅词>} <动词>,
也就是以一个名词或动词结尾,前面可以加上任意多个辅词。而句子就是要以名词短语开头,后面的名词短语和动词短语交替出现。
分析出语法结构,就可以进行动态规划。定义词性
j={0,1,2,3}。
f[i][0][k]表示前i个字母,以i结尾的单词词性为n,构成了k个句子的最小单词数
f[i][1][k]表示前i个字母,以i结尾的单词词性为v,构成了k个句子的最小单词数
f[i][2][k]表示前i个字母,以i结尾的单词词性为a,后面该接v了,构成了k个句子的最小单词数
f[i][3][k]表示前i个字母,以i结尾的单词词性为a,后面该接n了,构成了k个句子的最小单词数
枚举可能以i结尾的单词,设它的词性为type,前一个单词结尾为j。则分类讨论f[i][0/1/2/3][k]可以由f[j][0/1/2/3][k/k-1]的哪些转移而来,具体可以见这个人的分析
找到最小的k,使得min{f[m,0,k],f[m,1,k]}有意义,则最小的句子数为k,单词数为min{f[m,0,k],f[m,1,k]}
时间复杂度为O(nm·maxlen),匹配单词的时候可以用Trie树,第三维状态必须使用滚动数组。
tips:滚动数组每次都要清inf。初值为f[0][0][0]=0
#include <cstring>
#include <cstdio>
#define N 1005
#define M 6000
#define inf 0x3f3f3f3f
int n,m,maxlen,word[M][22],f[M][4][2],p;//word[i][j]表示以i开头,长度为j的子串是否为单词以及词性
//f[i][0][k]表示前i个字母,以i结尾的单词词性为n,构成了k个句子的最小单词数
//f[i][1][k]表示前i个字母,以i结尾的单词词性为v,构成了k个句子的最小单词数
//f[i][2][k]表示前i个字母,以i结尾的单词词性为a,后面该接v了,构成了k个句子的最小单词数
//f[i][3][k]表示前i个字母,以i结尾的单词词性为a,后面该接n了,构成了k个句子的最小单词数
char s[M];
bool blank=0;
inline int max(