HDU 2340 Obfuscation(dp)

题意:已知原串(长度为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[] = {0, 0, -1, 1};
const int dc[] = {-1, 1, 0, 0};
const double pi = acos(-1.0);
const double eps = 1e-8;
const int MAXN = 1000 + 10;
const int MAXT = 10000 + 10;
using namespace std;
char s[MAXN];
char x[MAXT][110];
int cnt[MAXN][30];
int dp[MAXN];
int pre[MAXN];
int markx[MAXN];
int marky[MAXN];
struct P{
    int len, id;
    char st;
    char num[30];
    P(int l, int e, char h):len(l), id(e), st(h){
        memset(num, 0, sizeof num);
        for(int i = 0; i < len; ++i){
            ++num[x[id][i] - 'a'];
        }
    }
};
vector<P> v[30];
bool judge(int a, int b, P &x){
    if(a == 0){
        for(int i = 0; i < 26; ++i){
            if(x.num[i] != cnt[b][i]) return false;
        }
        return true;
    }
    else{
        for(int i = 0; i < 26; ++i){
            if(x.num[i] != cnt[b][i] - cnt[a - 1][i]) return false;
        }
        return true;
    }
}
int main(){
    int T;
    scanf("%d", &T);
    while(T--){
        for(int i = 0; i < 30; ++i){
            v[i].clear();
        }
        memset(s, 0, sizeof s);
        memset(x, 0, sizeof x);
        memset(cnt, 0, sizeof cnt);
        memset(dp, 0, sizeof dp);
        memset(pre, -1, sizeof pre);//不能初始化为0,这样会漏掉第一个字母是第一个单词这种情况
        memset(markx, 0, sizeof markx);
        memset(marky, 0, sizeof marky);
        scanf("%s", s);//原串
        int l = strlen(s);
        ++cnt[0][s[0] - 'a'];
        for(int i = 1; i < l; ++i){//记录截止到原串中第i个字母时26个字母每个字母的个数
            for(int j = 0; j < 26; ++j){
                cnt[i][j] = cnt[i - 1][j];
            }
            ++cnt[i][s[i] - 'a'];
        }
        int n;
        scanf("%d", &n);
        for(int i = 0; i < n; ++i){
            scanf("%s", x[i]);
            int len = strlen(x[i]);
            char tmp = x[i][0];
            v[x[i][len - 1] - 'a'].push_back(P(len, i, tmp));//以单词的尾字母为下标,每个单词初始化长度,标号和首字母三个属性
        }
        for(int i = 0; i < l; ++i){//分别以原串中的每个字母为基础,在vector中查找以该字母为尾字母的单词,并且该单词的首字母在原串中该字母的位置之前出现
             int t = v[s[i] - 'a'].size();
             for(int j = 0; j < t; ++j){
                P& w = v[s[i] - 'a'][j];
                int tmp = i - w.len + 1;//该字母的首字母在原串中所对应的下标
                if(tmp == 0 && w.st == s[tmp] && judge(tmp, i, w)){//若查找到的这个单词是原串中的第一个单词且匹配
                    ++dp[i];
                    markx[i] = s[i] - 'a';
                    marky[i] = j;
                }
                else if(tmp > 0 && w.st == s[tmp] && judge(tmp, i, w) && dp[tmp - 1]){//tmp>0,不是tmp!=0
                    dp[i] += dp[tmp - 1];
                    pre[i] = tmp - 1;
                    markx[i] = s[i] - 'a';
                    marky[i] = j;
                }
             }
        }
        if(!dp[l - 1]){
            printf("impossible\n");
        }
        else if(dp[l - 1] > 1){
            printf("ambiguous\n");
        }
        else{
            stack <pair<int, int> > ss;
            while(!ss.empty()) ss.pop();
            for(int i = l - 1; i != -1; 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 0;
}
原文地址:https://www.cnblogs.com/tyty-Somnuspoppy/p/6030928.html