• 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;
    }
  • 相关阅读:
    jni基础
    Rank Scores
    LeetCode:Longest Substring Without Repeating Characters
    LeetCode: Two Sum
    vim配置
    设计模式眨一眨
    分布式时序数据库InfluxDB
    地图坐标转换
    根据两点间的经纬度计算距离
    解密经纬度数据(火星坐标)
  • 原文地址:https://www.cnblogs.com/tyty-Somnuspoppy/p/6030928.html
Copyright © 2020-2023  润新知