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