一中OJ #3529 解密游戏 [南开OJ P3824 重庆市四校联考T2] | 动态规划 + Trie树优化 | 解题报告

一中OJ | #3529 解密游戏 [南开OJ P3824 重庆市四校联考T2] | 动态规划 + Trie树

时限 1000MS/Case 内存 128MB/Case





题目描述

小南和小开特别喜欢玩解密游戏,轮到小南加密的时候,由于他的加密方式过于丧心病 狂,所以小开怎么也不能解密成功,于是她来找你帮忙。 

密文是一个长度为 n 的数字串,只由 0~9 之间的数字组成。 每个小写字母对应 0~9 之 间的一个数字。小南和小开共同拥有一本字典, 字典中有 m 个单词,每个单词长度不超过50。 

明文是一个数字,表示最少用多少个单词首尾拼接在一起,使得拼接而成的这个字符串可以表示密文(也即相同位置的字符串中字母对应数字跟密文相同)。单词可以重复使用。输出明文,如果无解的话明文为-1。

输入格式

第一行两个正整数 n,m。
第二行有 26 个数字,每个数字是 0~9 之间的数,分别表示字母 a~z 对应的数字。
第三行是长度为 n 的数字串,表示密文。
接下来 m 行,每行一个小写字母串,表示字典中的一个单词。

输出格式

输出一个整数,表示明文

样例输入

10 5

2 2 2 3 3 3 4 4 1 1 5 5 6 6 0 7 0 7 7 8 8 8 9 9 9 0
7325189087
it
your
reality
real
our

样例输出

2

数据范围

对于 30%的数据: 1 ≤ n, m ≤ 1000。

对于 100%的数据: 1 ≤ n, m ≤ 10^5。

样例解释

我们最少可以用两个单词 reality our,组成的字符串 realityour 去表示密文。

———————————————————-

题目分析

首先分析一下题意…题意大概是这样的,给你一堆字符串,然后一个映射表可以把这些字符串映射成一堆数字串

现在给你一个大数字串,请问最少用多少个可以重复的小数字串可以组成大数字串?

设f[i]为大数字串中前i个数字最少需要用f[i]个小数字串组成,不难写出状态转移方程:f[i]=min{f[j-1]+1 | a[i]~a[j]是一个小数字串}

用Trie树来存储小数字串,每次截出大数字串中的一段在Trie树中匹配

有一个实现技巧,就是把小数字串reverse存储在Trie树中,那么在转移f[i]时,不需要每次重新截子串串头,直接把f[i]作为串头,按位向前搜索,可以节省时间

———————————————————-

代码

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <cctype>
#include <iomanip>
#define inf 0x7f7f7f7f
using namespace std;
int trie[6000005][10],maptable[30],n,m,np=0,f[100005],ans=inf;
bool val[6000005];
char in[55],pwd[100005],rev[100005];
void add(char s[55])
{
	int now=0;
	for(int i=strlen(s)-1;i>=0;i--)
	{
		if(!trie[now][maptable[s[i]-'a'+1]])
			trie[now][maptable[s[i]-'a'+1]]=++np;
		now=trie[now][maptable[s[i]-'a'+1]];
	}
	val[now]=true;
	return;
}
bool query(char s[55])//普通的查找 
{
	int now=0;
	for(int i=0;i<sizeof(s);i++)
	{
		if(!trie[now][maptable[s[i]-'a'+1]])
			return false;
		now=trie[now][maptable[s[i]-'a'+1]];
	}
	return val[now];	
}
int proc(int pos)//优化的查找 
{
	int ret=inf,now=0;
	for(int l=0;l<50 && pos-l>=0;l++)
	{
		if(val[now]) ret=min(ret,f[pos-l]+1);
		if(!trie[now][pwd[pos-l]]) break;
		now=trie[now][pwd[pos-l]];
		if(val[now]) ret=min(ret,f[pos-l-1]+1);
	}
	return ret;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=26;i++) scanf("%d",&maptable[i]);
	scanf("%s",pwd+1);
	for(int i=1;i<=m;i++)
	{
		scanf("%s",in);
		add(in);
	}
	for(int i=1;i<=n;i++)
	{
		f[i]=inf;
		pwd[i]-='0';
	}
	for(int i=1;i<=n;i++)
		f[i]=proc(i);
	ans=f[n];
	if(ans>=inf) ans=-1;
	cout<<ans<<endl;
	return 0;
}
    原文作者:Trie树
    原文地址: https://blog.csdn.net/OoLuoChenoO/article/details/79285850
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞