C. Crossword Validation(字典树)

Problem - C - Codeforces

题意:

你得到了一个在N×N网格上完成的填字游戏。每个单元格要么是填有字母的白色单元格,要么是黑色单元格。你还会得到一本包含M个不同单词的字典,其中每个单词都有一个与之相关的分数。网格中的一个横向候选词是在网格的同一行上的一串连续的字母,不能被扩展。从形式上看,这意味着候选词最左边的单元格要么不存在,要么是一个黑色单元格,最右边的单元格也是如此。垂直候选词的定义与此类似,只是字母在同一列。候选词对于水平词来说是从左到右阅读,对于垂直词来说是从上到下阅读。当且仅当每个候选词都出现在给定的词典中时,该填字游戏才是有效的。一个有效的字谜的分数是字典中与每个候选词相关的分数之和。请注意,两个或多个候选词可能是相同的。在这种情况下,该词的分数会相应地被多次加分。你的程序必须确定谜题的分数,否则就报告说它无效。

输入
输入包含多个案例。输入的第一行包含一个正整数T,即案例的数量。

对于每个案例,输入的第一行包含两个整数N,M(1≤N≤1000,1≤M≤4×106),即网格的大小和字典中的单词数。接下来的N行各包含一个长度为N的字符串,其中每个字符是小写英文字母或'#'。如果第i个字符串中的第j个字符是'#',那么位于第i行和第j列交汇处的单元格就是一个黑色单元。否则,这个字符表示该单元格中填入的字母。下面的M行,每一行都包含一个只由小写英文字母组成的非空字符串,以及一个正整数,表示字典中的一个词和它的分数。保证这M个词是成对的,每个词的长度不超过N,每个词的分数不超过109。

保证所有情况下N2的总和不超过4×106,所有情况下字典中所有单词的长度之和不超过4×106。

输出
对于每个案例,在单行中打印一个整数,即输入中给出的字谜解决方案的分数,如果它是有效的。如果解决方案无效,则打印-1。

题解:

根据题意,看每行每列的最长字符串是否在所给的字符串中全都出现过

所以我们应该先构造一个根据所给字符串的字典树

接着枚举所有列和行的的最长字符串看是否在我们构建的字典树中出现过,

出现过加上,没直接输出-1即可

代码有三点需要注意的

1.注意初始化,初始化掉之前变化过的即可,否则会t

2.一个知识点,如果字符串的第i位赋值为0(int),相当于在i处加了一个终止符,无论是输入,还是输出,还是传递,到i都会停止,当然如果原本i后面有值,依旧存在

3.因为在询问的过程中,有可能出现,所询问字符串,在一个所给字符串的子串中的情况

所以,还要判断赋值是否为0

#include<iostream>
#include<cstring>
using namespace std;
int n,m;
char mp[1005][10005];
char s[1005];
int idx;
int cnt[4000050];
int tre[4000050][30];
void insert(char t[1005],int v)
{
	int len = strlen(t);
	int now = 0;
	for(int i = 0;i < len;i++)
	{
		int ne = t[i]-'a';
		if(!tre[now][ne])
		tre[now][ne] = ++idx;
		now = tre[now][ne];
	}
	cnt[now] += v;
}
int query(char t[1005])
{
	int len = strlen(t);
	int now = 0;
	for(int i = 0;i < len;i++)
	{
		int ne = t[i] - 'a';
		if(!tre[now][ne])
		return  -1;
		now = tre[now][ne];
	}
	if(!cnt[now])//存在一个字典中存在一个子串为t的字符串,所以无值 
	return  -1;
	else
	return cnt[now];
}
void solve()
{
	cin >>n >> m;
	for(int i = 0;i <= idx;i++)
	{
		memset(tre[i],0,sizeof tre[i]); 
		cnt[i] = 0;
	}
	idx = 0;
	// 初始部分即可,不然会t 
	
	
	
	
	
	for(int i = 1;i <= n;i++)
	{
		cin >> mp[i]+1;
	}
	for(int i = 1;i <= m;i++)
	{
		int v;
		cin >> s>>v;
		insert(s,v);
	}
	long long ans = 0;
	int f = 0;
	for(int i = 1;i <= n;i++)
	{
		for(int j = 1;j <= n;j++)
		{
			if(mp[i][j] == '#')
			continue;
			int k = 0;
			while(mp[i][j] != '#'&&j <= n)
			{
				s[k++] = mp[i][j++];
			}
			s[k] = 0;//一个知识点,如果字符串的第i位赋值为0(int),相当于在i处加了一个终止符,无论是输入,还是输出,还是传递,到i都会停止,当然如果原本i后面有值,依旧存在
            int p = query(s);
	        if(p == -1)
			{
				f = 1;
			} 
			else
			{
				ans += p; 
			}
			
		}
	}
	
	for(int j = 1;j <= n;j++)
	{
		for(int i = 1;i <= n;i++)
		{
			if(mp[i][j] == '#')
			{
				continue;
			}
			int k = 0;
			while(mp[i][j]!='#'&&i <= n)
			{
				s[k++] = mp[i++][j];
			}
			s[k] = 0;
			int p = query(s);
			if(p == -1)
			f= 1;
			else
			ans += p;
		}
	}
	if(f)
	{
		cout<<-1<<"\n";
	}
	else
	{
		cout<<ans<<"\n";
	}
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	int t;
	cin >> t;
	while(t--)
	{
		solve();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值