题意:已知原串(长度为1~1000),它由多个单词组成,每个单词除了首尾字母,其余字母为乱序,且句子中无空格。给定n个互不相同的单词(1 <= n <= 10000),问是否能用这n个单词还原出这个句子。

eg:

3
tihssnetnceemkaesprfecetsesne
5
makes
perfect
sense
sentence
this
hitehre
应输出唯一解:this sentence makes perfect sense

分析:

1、将原串从头到尾遍历,分别以原串中的每个字母为基础,查找是否有以该字母为尾字母的单词,并判断该单词是否可以在原串的这个位置形成一种构造方法,dp[i]表示从开始到第 i 位有几种构成方法,因此,状态转移方程为dp[j] += dp[i - 1](若 i ~ j 位可以构成一个单词)

2、为了减小枚举量,通过num[]记录每个单词的字母个数,并用cnt[][]统计了元传中截止到每一位时各个字母的个数;

3、边dp边记录pre[i],以便最终输出符合要求的句子。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<iostream>
#include<sstream>
#include<iterator>
#include<algorithm>
#include<string>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<deque>
#include<queue>
#include<list>
typedef long long ll;
typedef unsigned long long llu;
const int INT_INF = 0x3f3f3f3f;
const int INT_M_INF = 0x7f7f7f7f;
const ll LL_INF = 0x3f3f3f3f3f3f3f3f;
const ll LL_M_INF = 0x7f7f7f7f7f7f7f7f;
const int dr[] = {, , -, };
const int dc[] = {-, , , };
const double pi = acos(-1.0);
const double eps = 1e-;
const int MAXN = + ;
const int MAXT = + ;
using namespace std;
char s[MAXN];
char x[MAXT][];
int cnt[MAXN][];
int dp[MAXN];
int pre[MAXN];
int markx[MAXN];
int marky[MAXN];
struct P{
int len, id;
char st;
char num[];
P(int l, int e, char h):len(l), id(e), st(h){
memset(num, , sizeof num);
for(int i = ; i < len; ++i){
++num[x[id][i] - 'a'];
}
}
};
vector<P> v[];
bool judge(int a, int b, P &x){
if(a == ){
for(int i = ; i < ; ++i){
if(x.num[i] != cnt[b][i]) return false;
}
return true;
}
else{
for(int i = ; i < ; ++i){
if(x.num[i] != cnt[b][i] - cnt[a - ][i]) return false;
}
return true;
}
}
int main(){
int T;
scanf("%d", &T);
while(T--){
for(int i = ; i < ; ++i){
v[i].clear();
}
memset(s, , sizeof s);
memset(x, , sizeof x);
memset(cnt, , sizeof cnt);
memset(dp, , sizeof dp);
memset(pre, -, sizeof pre);//不能初始化为0,这样会漏掉第一个字母是第一个单词这种情况
memset(markx, , sizeof markx);
memset(marky, , sizeof marky);
scanf("%s", s);//原串
int l = strlen(s);
++cnt[][s[] - 'a'];
for(int i = ; i < l; ++i){//记录截止到原串中第i个字母时26个字母每个字母的个数
for(int j = ; j < ; ++j){
cnt[i][j] = cnt[i - ][j];
}
++cnt[i][s[i] - 'a'];
}
int n;
scanf("%d", &n);
for(int i = ; i < n; ++i){
scanf("%s", x[i]);
int len = strlen(x[i]);
char tmp = x[i][];
v[x[i][len - ] - 'a'].push_back(P(len, i, tmp));//以单词的尾字母为下标,每个单词初始化长度,标号和首字母三个属性
}
for(int i = ; i < l; ++i){//分别以原串中的每个字母为基础,在vector中查找以该字母为尾字母的单词,并且该单词的首字母在原串中该字母的位置之前出现
int t = v[s[i] - 'a'].size();
for(int j = ; j < t; ++j){
P& w = v[s[i] - 'a'][j];
int tmp = i - w.len + ;//该字母的首字母在原串中所对应的下标
if(tmp == && w.st == s[tmp] && judge(tmp, i, w)){//若查找到的这个单词是原串中的第一个单词且匹配
++dp[i];
markx[i] = s[i] - 'a';
marky[i] = j;
}
else if(tmp > && w.st == s[tmp] && judge(tmp, i, w) && dp[tmp - ]){//tmp>0,不是tmp!=0
dp[i] += dp[tmp - ];
pre[i] = tmp - ;
markx[i] = s[i] - 'a';
marky[i] = j;
}
}
}
if(!dp[l - ]){
printf("impossible\n");
}
else if(dp[l - ] > ){
printf("ambiguous\n");
}
else{
stack <pair<int, int> > ss;
while(!ss.empty()) ss.pop();
for(int i = l - ; i != -; i = pre[i]){//##########
ss.push(pair<int, int>(markx[i], marky[i]));
}
bool flag = true;
while(!ss.empty()){
pair<int, int> w = ss.top();
ss.pop();
if(flag) flag = false;
else printf(" ");
printf("%s", x[v[w.first][w.second].id]);
}
printf("\n");
}
}
return ;
}

HDU 2340 Obfuscation(dp)的更多相关文章

  1. HDU 4433 locker(DP)(2012 Asia Tianjin Regional Contest)

    Problem Description A password locker with N digits, each digit can be rotated to 0-9 circularly.You ...

  2. HDU 3008 Warcraft(DP)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3008 题目大意:人有100血和100魔法,每秒增加 t 魔法(不能超过100).n个技能,每个技能消耗 ...

  3. hdu 2059 龟兔赛跑(dp)

    龟兔赛跑 Problem Description 据说在很久很久以前,可怜的兔子经历了人生中最大的打击——赛跑输给乌龟后,心中郁闷,发誓要报仇雪恨,于是躲进了杭州下沙某农业园卧薪尝胆潜心修炼,终于练成 ...

  4. HDU 4832 Chess (DP)

    Chess Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  5. HDU 4945 2048(dp)

    题意:给n(n<=100,000)个数,0<=a[i]<=2048 .一个好的集合要满足,集合内的数可以根据2048的合并规则合并成2048 .输出好的集合的个数%998244353 ...

  6. hdu 2571 命运(dp)

    Problem Description 穿过幽谷意味着离大魔王lemon已经无限接近了! 可谁能想到,yifenfei在斩杀了一些虾兵蟹将后,却再次面临命运大迷宫的考验,这是魔王lemon设下的又一个 ...

  7. HDU 6170----Two strings(DP)

    题目链接 Problem Description Giving two strings and you should judge if they are matched.The first strin ...

  8. HDU 2159 FATE (dp)

    FATE Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submissi ...

  9. HDU 2059 龟兔赛跑 (dp)

    题目链接 Problem Description 据说在很久很久以前,可怜的兔子经历了人生中最大的打击--赛跑输给乌龟后,心中郁闷,发誓要报仇雪恨,于是躲进了杭州下沙某农业园卧薪尝胆潜心修炼,终于练成 ...

随机推荐

  1. c#加密 可逆与不可逆MD5 加密

    1.方法一 (不可逆加密) srxljl public string EncryptPassword(string PasswordString,string PasswordFormat )     ...

  2. phpcms v9和discuz X3.1实现同步登陆退出论坛(已实现)

    网络上文章很多,按步骤配置好了之后phpcms可以同步登录dz,但是dz登录后状态却无法同步到phpcms,网络上找了很多资料都大同小异,头大.只能自己调试了,废话不多说了.       以下网络上抄 ...

  3. 经常使用的android弹出对话框

    我们在平时做开发的时候,免不了会用到各种各样的对话框,相信有过其它平台开发经验的朋友都会知道,大部分的平台都仅仅提供了几个最简单的实现,假设我们想实现自己特定需求的对话框,大家可能首先会想到,通过继承 ...

  4. C#二维数组(矩形数组,交错数组)

    C# 支持一维数组.多维数组(矩形数组)和数组的数组(交错的数组) 1.多维数组 声明:string[,] names; 初始化:int[,] numbers = new int[3, 2] { {1 ...

  5. 高级进程间通信之基于STREAMS的管道

    基于STREAMS的管道(简称STREAMS管道,STREAMS pipe)是一个双向(全双工)管道.单个STREAMS管道就能向父.子进程提供双向的数据流. 将http://www.cnblogs. ...

  6. 关于php配置文件

    一:配置文件(php.ini)在 PHP 启动时被读取.对于服务器模块版本的 PHP,仅在 web 服务器启动时读取一次.对于 CGI 和 CLI 版本,每次调用都会读取. 二:Note that t ...

  7. Redis 客户端连接

      Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 socket 会被设置为非阻 ...

  8. [Java] HashMap、TreeMap、Hashtable排序

    Java中对Map(HashMap,TreeMap,Hashtable等)的排序时间 首先简单说一下他们之间的区别: HashMap: 最常用的Map,它根据键的HashCode 值存储数据,根据键可 ...

  9. css笔记18:盒子模型案例分析示范

  10. linux命令行操作快捷键

    在shell命令终端中,Ctrl+n相当于方向向下的方向键,Ctrl+p相当于方向向上的方向键. 在命令终端中通过它们或者方向键可以实现对历史命令的快速查找.这也是快速输入命令的技巧. 在命令终端中可 ...