NC16863. [NOI2000]古城之谜
描述
著名的考古学家石教授在云梦高原上发现了一处古代城市遗址。让教授欣喜的是在这个他称为冰峰城(Ice-Peak City)的城市中有12块巨大石碑,上面刻着用某种文字书写的资料,他称这种文字为冰峰文。然而当教授试图再次找到冰峰城时,却屡屡无功而返。
幸好当时教授把石碑上的文字都拍摄了下来,为了解开冰峰城的秘密,教授和他的助手牛博士开始研究冰峰文,发现冰峰文只有陈述句这一种句型和名词(n)、动词(v)、辅词(a)这三类单词,且其文法很简单:
<文章> ::= <句子> { <句子> }
<句子> ::= <陈述句>
<陈述句> ::= <名词短语> { <动词短语> <名词短语> } [ <动词短语> ]
<名词短语> ::= <名词> | [ <辅词> ] <名词短语>
<动词短语> ::= <动词> | [ <辅词> ] <动词短语>
<单词> ::= <名词> | <动词> | <辅词>
注:其中<名词>、<动词>和<辅词>由词典给出,“::=”表示定义为,“|”表示或,{}内的项可以重复任意多次或不出现,[]内的项可以出现一次或不出现。
在研究了大量资料后,他们总结了一部冰峰文词典,由于冰峰文恰好有26个字母,为了研究方便,用字母a到z表示它们。
冰峰文在句子和句子之间以及单词和单词之间没有任何分隔符,因此划分单词和句子令石教授和牛博士感到非常麻烦,于是他们想到了使用计算机来帮助解决这个问题。假设你接受了这份工作,你的第一个任务是写一个程序,将一篇冰峰文文章划分为最少的句子,在这个前提下,将文章划分为最少的单词。
输入描述
输入第1行为词典中的单词数n(n<=1000)。
输入第2行至第(n+1)行每行表示一个单词,形为“α.mot”, α表示词性,可能是n(名词),v(动词),a(辅词)中的一个,mot为单词,单词的长度不超过20。拼写相同而词性不同的单词视为不同的单词,如输入示例中的n.kick与v.kick是两个不同的单词。
输入第(n+2)行为需要划分的文章,以“.”结束。
l输入中的文章确保为冰峰文。文章是由有限个句子组成的,每个句子只包含有限个单词。文章长度不超过5KB。
输出描述
为两行,每行一个整数。
第1行为划分出来的句子数。
第2行为划分出来的单词数
示例1
输入:
11 n.table n.baleine a.silly n.snoopy n.sillysnoopy v.is v.isnot n.kick v.kick a.big v.cry sillysnoopyisnotbigtablebaleinekicksnoopysillycry.
输出:
2 9
说明:
(为了阅读方便,划分的单词用空格分隔,在单词的右上角标出它的词性,每行写一个句子,用句号表示句子结束。)
输出对应的划分:
sillysnoopyn isnotv biga tablen.
baleinen kickv snoopyn sillya cryv.
如果用下面的划分:
sillya snoopyn isnotv biga tablen.
baleinen kickv snoopyn sillya cryv.
则划分的句子数仍为2个,但单词数却多了1个,为10个,显然应该按前者而不是后者划分。
C++11(clang++ 3.9) 解法, 执行用时: 361ms, 内存消耗: 600K, 提交时间: 2020-08-24 20:16:09
#include <bits/stdc++.h> using std::string; using std::vector; using std::map; using std::min; typedef string str; const int N = 5500; const int INF1 = 0x3f3f3f3f; struct node{ str s; int x; friend bool operator < (const node &lhs, const node &rhs) { return lhs.s < rhs.s; } }; vector<node> v; struct node1{ int s, w; }; node1 f[N][2]; map<str, int> dic; int n; int main(){ //freopen("INPUT.008", "r", stdin); scanf("%d", &n); for (int i = 1; i <= n; ++i) { node tmp; str s; std::cin >> s; if (s[0] == 'n') tmp.x = 1; if (s[0] == 'v') tmp.x = 2; if (s[0] == 'a') tmp.x = 4; tmp.s=""; for (int i = 2; i < s.length(); ++i) tmp.s += s[i]; v.push_back(tmp); } std::sort(v.begin(), v.end()); node tmp; tmp.s = '$'; v.push_back(tmp); int val = 0; for (int i = 0; i < v.size() - 1; ++i) { val |= v[i].x; if (v[i].s != v[i + 1].s) { dic.insert(std::pair<str, int>(v[i].s, val)); val = 0; } } str text; std::cin >> text; text = "$" + text; int l = text.length(); for (int i = 0; i < l - 1; ++i) for (int j = 0; j < 2; ++j) f[i][j].s = f[i][j].w = INF1; f[0][0].s = f[0][0].w = 0; for (int i = 0; i < l - 2; ++i) { for (int j = 0; j < 2; ++j) { if (f[i][j].s == INF1) continue; str s = ""; for (int k = i + 1; k < l - 1; ++k) { s += text[k]; auto it = dic.find(s); if (it == dic.end()) continue; if (it->second & 1) { int z = j ? 0 : 1; if (f[k][0].s > f[i][j].s + z) { f[k][0].s = f[i][j].s + z; f[k][0].w = f[i][j].w + 1; } else if (f[k][0].s == f[i][j].s + z && f[k][0].w > f[i][j].w + 1) f[k][0].w = f[i][j].w + 1; } if (it->second & 2 && i != 0 && j != 1) { if (f[k][1].s > f[i][j].s) { f[k][1].s = f[i][j].s; f[k][1].w = f[i][j].w + 1; } else if (f[k][1].s == f[i][j].s && f[k][1].w > f[i][j].w + 1) f[k][1].w = f[i][j].w + 1; } if (it->second & 4 && k != l - 2) { if (f[k][j].s > f[i][j].s) { f[k][j].s = f[i][j].s; f[k][j].w = f[i][j].w + 1; } else if (f[k][j].s == f[i][j].s && f[k][j].w > f[i][j].w + 1) f[k][j].w = f[i][j].w + 1; } } } } if (f[l - 2][0].s < f[l - 2][1].s) printf("%d\n%d\n", f[l - 2][0].s, f[l - 2][0].w); else if (f[l - 2][0].s > f[l - 2][1].s) printf("%d\n%d\n", f[l - 2][1].s, f[l - 2][1].w); else printf("%d\n%d\n", f[l - 2][0].s, min(f[l - 2][0].w, f[l - 2][1].w)); return 0; }
C++14(g++5.4) 解法, 执行用时: 4ms, 内存消耗: 1380K, 提交时间: 2020-08-24 17:49:27
#include<bits/stdc++.h> #define ri register int #define inf 0x3f3f3f3f using std::min; using std::max; using std::pair; using std::make_pair; const int MAXN=1010; const int MAXM=5010; int n,m; char buf[30]; char s[MAXM]; pair<int,int> f[MAXM][4]; //0:n. //1:v. //2:n. with a. //3:v. with a. struct trie { int sz; struct node { int v,ch[26]; }a[MAXN*20]; void insert(char *s,int len,int v) { ri u=0,son; for(ri i=0;i<len;++i) { son=s[i]-'a'; if(!a[u].ch[son])a[u].ch[son]=++sz; u=a[u].ch[son]; } a[u].v|=v; } int find(char *s,int len) { ri u=0,son; for(ri i=0;i<len;++i) { son=s[i]-'a'; if(!a[u].ch[son])return 0; u=a[u].ch[son]; } return a[u].v; } }tree; int main() { // freopen("in.in","r",stdin); scanf("%d",&n); for(ri i=1,v;i<=n;++i) { memset(buf,0,sizeof(buf)); scanf("%s",buf+1); if(buf[1]=='n')v=1; else if(buf[1]=='v')v=2; else v=4; tree.insert(buf+3,strlen(buf+3),v); } scanf("%s",s+1); m=strlen(s+1)-1; for(ri i=0;i<=m;++i) for(ri j=0;j<4;++j) f[i][j]=make_pair(inf,inf); f[0][0]=make_pair(0,0); for(ri i=1,v;i<=m;++i) { for(ri j=i-1;j>=max(0,i-20);--j) { v=tree.find(s+j+1,i-j); pair<int,int> p0=min(f[j][0],f[j][2]),p1=min(f[j][1],f[j][3]); if(v&1) { f[i][0]=min(f[i][0],make_pair(p0.first+1,p0.second+1)); f[i][0]=min(f[i][0],make_pair(p1.first ,p1.second+1)); } if(v&2 && j!=0) { f[i][1]=min(f[i][1],make_pair(p0.first ,p0.second+1)); f[i][1]=min(f[i][1],make_pair(inf ,inf )); } if(v&4) { f[i][2]=min(f[i][2],make_pair(p0.first ,p0.second+1)); f[i][3]=min(f[i][3],make_pair(p1.first ,p1.second+1)); } } // for(ri j=0;j<4;++j) // printf("f[%d][%d]=(%d,%d)\n",i,j,f[i][j].first,f[i][j].second); } pair<int,int> ans=min(f[m][0],f[m][1]); printf("%d\n%d\n",ans.first,ans.second); return 0; }