列表

详情


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;
}

上一题