100723H Obfuscation
题目大意
给你一个包含n 个单词的字典,给你一篇文章,文章包括若干词典里的单词,把句子里的空格都去掉,单词的首位字母都不变,中间的字符集为乱序,问能否恢复这篇文章,使得单词都是词典里的单词,如果有唯一解,输出唯一解。
分析
可以将将一段字符串哈希来确定这段字符串的字母组成,在记录每一段的首字母和尾字母,这样便可以将整段字符表示出来了,在进行完预处理之后我们进行dp,用dpi表示i+1到m这一段的字符可以由先有字母表组成几个。最后如果dp0有0个则代表无法组成,大于1则有歧义,等于一则根据之前记录的nxt数组和每一段字符对应的编号将答案输出。详见代码。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define sp cout<<"---------------------------------------------------"<<endl
#define uli unsigned long long
#define li long long
char bs[10002],s[10002][102];
uli wsh[30];
int len[10002],dp[1002],nxt[1002];
map<uli,int>mp[28][28];
multiset<uli>HASH[28][28];
int main(){
srand(time(0)+1010101);
int n,m,i,j,t;
for(i=0;i<28;i++)
wsh[i]=(uli)rand()*rand()*rand()*rand()*rand()*rand()*rand()*rand();
scanf("%d",&t);
while(t--){
for(i=0;i<26;i++)
for(j=0;j<26;j++){
HASH[i][j].clear();
mp[i][j].clear();
}
scanf("%s",bs);
scanf("%d",&n);
m=strlen(bs);
for(i=1;i<=n;i++){
scanf("%s",s[i]);
len[i]=strlen(s[i]);
uli hsh=0;
for(j=0;j<len[i];j++)
hsh+=wsh[s[i][j]-'a'];
HASH[s[i][0]-'a'][s[i][len[i]-1]-'a'].insert(hsh);
mp[s[i][0]-'a'][s[i][len[i]-1]-'a'][hsh]=i;
}
memset(dp,0,sizeof(dp));
memset(nxt,0,sizeof(nxt));
dp[m]=1;
for(i=m-1;i>=0;i--){
uli hsh=0;
for(j=i;j<m;j++){
hsh+=wsh[bs[j]-'a'];
int x=HASH[bs[i]-'a'][bs[j]-'a'].count(hsh);
if(x>0&&dp[j+1]){
dp[i]+=x*dp[j+1];
nxt[i]=j+1;
}
}
dp[i]=min(dp[i],2);
}
if(dp[0]==0)puts("impossible");
else if(dp[0]>1)puts("ambiguous");
else {
for(i=0;i<m;i=nxt[i]){
uli hsh=0;
for(j=i;j<nxt[i];j++)
hsh+=wsh[bs[j]-'a'];
printf("%s ",s[mp[bs[i]-'a'][bs[nxt[i]-1]-'a'][hsh]]);
}
puts("");
}
}
return 0;
}