Sicily 1008. Translations

本文介绍了一种解决翻译文件中双词短语自动重构的问题,通过构建和匹配两个排序列表来实现从样本短语及其翻译中重建原始翻译的过程。详细解释了输入格式、输出要求以及算法实现,包括使用深度优先搜索(DFS)进行匹配,并通过剪枝优化减少计算复杂度。

开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

1008. Translations

Constraints

Time Limit: 1 secs, Memory Limit: 32 MB

Description

Bob Roberts is in charge of performing translations of documents between various languages. To aid him in this endeavor his bosses have provided him with translation files. These files come in twos -- one containing sample phrases in one of the languages and the other containing their translations into the other language. However, some over-zealous underling, attempting to curry favor with the higher-ups with his initiative, decided to alphabetically sort the contents of all of the files, losing the connections between the phrases and their translations. Fortunately, the lists are comprehensive enough that the original translations can be reconstructed from these sorted lists. Bob has found this is most usually the case when the phrases all consist of two words. For example, given the following two lists: Language 1 Phrases Language 2 Phrases arlo zym bus seat flub pleve bus stop pleve dourm hot seat pleve zym school bus Bob is able to determine that arlo means hot, zym means seat, flub means school, pleve means bus, and dourm means stop. After doing several of these reconstructions by hand, Bob has decided to automate the process. And if Bob can do it, then so can you.

Input

Input will consist of multiple input sets. Each input set starts with a positive integer n, n 250, indicating the number of two-word phrases in each language. This is followed by 2n lines, each containing one two-word phrase: the first n lines are an alphabetical list of phrases in the first language, and the remaining n lines are an alphabetical list of their translations into the second language. Only upper and lower case alphabetic characters are used in the words. No input set will involve more than 25 distinct words. No word appears as the first word in more than 10 phrases for any given language; likewise, no word appears as the last word in more than 10 phrases. A line containing a single 0 follows the last problem instance, indicating end of input.

Output

For each input set, output lines of the form word1/word2 where word1 is a word in the first language and word2 is the translation of word1 into the second language, and a slash separates the two. The output lines should be sorted according to the first language words, and every first language word should occur exactly once. There should be no white space in the output, apart from a single blank line separating the outputs from different input sets. Imitate the format of the sample output, below. There is guaranteed to be a unique correct translation corresponding to each input instance.

Sample Input

4
arlo zym
flub pleve
pleve dourm
pleve zym
bus seat
bus stop
hot seat
school bus
2
iv otas
otas re
ec t
eg ec
0

Sample Output

arlo/hot
dourm/stop
flub/school
pleve/bus
zym/seat

iv/eg
otas/ec
re/t
刚开始隐隐约约觉得要构图,但是在图这方面我的知识比较匮乏,只好试试硬着头皮DFS,也考虑到了剪枝,但由于没有构图,深搜的深度是n,TLE了。

参考了网上的题解,构图判断两图是否同构。

#include <algorithm>
#include <iostream>
#include <string>
#include <stdio.h>
#include <queue>
#include <string.h>
#include <vector>
#include <iomanip>
#include <map>
#include <stack>
#include <functional>
#include <list>
#include <cmath>
using namespace std;

#define MAX_N 30  // 最大点数

struct Node {  // 点
    string s;  // 点中信息
    int match;  // 匹配点在另一张图中的编号
    int inDegree;  // 入度
    int outDegree;  // 出度
    Node() {}
    Node(string sIn, int m = -1, int i = 0, int o = 0) {
        s = sIn;
        match = m;
        inDegree = i;
        outDegree = o;
    }
};

Node N1[MAX_N], N2[MAX_N];  // 图一图二的点
bool G1[MAX_N][MAX_N], G2[MAX_N][MAX_N];  // 图一图二
int n, n1, n2;  // n为题意n,n1为图一点数,n2为图二点数
map<string, int> M1, M2;  // 图一图二字符串和下标的对应关系
bool succeed;  // 是否搜到了答案

void makeG() {

    M1.clear();
    M2.clear();
    n1 = n2 = 0;
    memset(G1, false, sizeof(G1));
    memset(G2, false, sizeof(G2));

    for (int i = 0; i < n; i++) {  // 建图一
        string s1, s2;
        cin >> s1 >> s2;
        if (M1.find(s1) == M1.end()) {
            M1[s1] = n1;
            N1[n1] = Node(s1, -1, 0, 1);
            n1++;
        } else {
            N1[M1[s1]].outDegree++;
        }
        if (M1.find(s2) == M1.end()) {
            M1[s2] = n1;
            N1[n1] = Node(s2, -1, 1, 0);
            n1++;
        } else {
            N1[M1[s2]].inDegree++;
        }
        G1[M1[s1]][M1[s2]] = true;
    }

    for (int i = 0; i < n; i++) {  // 建图二
        string s1, s2;
        cin >> s1 >> s2;
        if (M2.find(s1) == M2.end()) {
            M2[s1] = n2;
            N2[n2] = Node(s1, -1, 0, 1);
            n2++;
        } else {
            N2[M2[s1]].outDegree++;
        }
        if (M2.find(s2) == M2.end()) {
            M2[s2] = n2;
            N2[n2] = Node(s2, -1, 1, 0);
            n2++;
        } else {
            N2[M2[s2]].inDegree++;
        }
        G2[M2[s1]][M2[s2]] = true;
    }
}

void cut() {  // 深搜前剪枝
    for (int j, i = 0, k; i < n1; i++) {
        int sameDegreeNum = 0;
        for (j = 0; j < n2; j++) {
            if (N1[i].inDegree == N2[j].inDegree && N1[i].outDegree == N2[j].outDegree) {
                k = j;
                sameDegreeNum++;
            }
        }
        if (sameDegreeNum == 1) {  // 如果图一图二中有且仅有两点i、j且i与j的出度入度相同,那么ij必然为对应点
            N1[i].match = k;
            N2[k].match = i;
        }
    }
}

void DFS(int pos) {
    if (pos == n1) {
        succeed = true;
        return;
    }
    if (N1[pos].match != -1) {  // 如果这个点已经匹配完成(cut函数剪枝)
        DFS(pos + 1);
        return;
    }
    for (int i = 0; i < n2; i++) {
        if (N2[i].match == -1 && N1[pos].inDegree == N2[i].inDegree && N1[pos].outDegree == N2[i].outDegree) {

            int j, k;
            for (j = 0; j < n1; j++) {  // 如果点pos和点i是可以匹配的,但是在图一中有一个点j,pos连接到j,且j有在图二中的对应点,但是点i并不连接到j的对应点,那么这是不同构的
                if (j != pos && G1[pos][j] && N1[j].match != -1 && !G2[i][N1[j].match]) break;
            }
            for (k = 0; k < n1; k++) {  // 如果点pos和点i是可以匹配的,但是在图一中有一个点j,j连接到pos,且j有在图二中的对应点,但是j的对应点并不连接到点i,那么这是不同构的
                if (k != pos && G1[k][pos] && N1[k].match != -1 && !G2[N1[k].match][i]) break;
            }

            if (j == n1 && k == n1) {  // 以上两种不同构的情况都没有出现,继续
                N1[pos].match = i;
                N2[i].match = pos;
                DFS(pos + 1);
                if (succeed) return;
                N1[pos].match = N2[i].match = -1;
            }
        }
    }
}

int main() {

    std::ios::sync_with_stdio(false);

    bool firstTime = true;

    while (1) {
        cin >> n;
        if (n == 0) break;

        if (!firstTime) cout << endl;
        firstTime = false;

        makeG();
        cut();
        succeed = false;
        DFS(0);
        for (map<string, int>::iterator iter = M1.begin(); iter != M1.end(); iter++) {
            cout << iter->first << "/" << N2[N1[iter->second].match].s << endl;
        }
    }

    //getchar();
    //getchar();
    
    return 0;
}                              


开发板推荐:天空星STM32F407VET6开发板

超高性价比 STM32主控 | 超高主频 | 一板兼容百芯 | 比赛神器 | 沉金彩色丝印

内容概要:本文详细记录了对一个Android ARM64静态ELF文件中字符串加密机制的逆向分析过程。该ELF文件的所有字符串均被加密,无法通过常规strings命令或IDA直接识别。作者通过分析发现,加密字符串存储在.rodata段,其解密所需信息(包括密文地址、长度和16位密钥)保存在.data.rel.ro段的40字节描述符中。核心解密函数sub_10F408采用自反的双pass流密码算法,结合固定密钥KEY_TERM(由.data段24字节数据计算得出),实现字节级非线性、位置与长度相关的加密。文章还复现了完整的Python解密脚本,并揭示了该保护机制的本质为代码混淆而非强加密,最终成功批量解密全部956条字符串,暴露程序真实行为,如shell命令模板、设备标识篡改、网络重置等操作。此外,文中还提及未启用的自定义壳框架及其反dump设计。; 适合人群:具备逆向工程基础的安全研究人员、二进制分析人员及对ELF保护技术感兴趣的开发者。; 使用场景及目标:①学习ELF二进制中字符串加密的典型实现方式与逆向突破口;②掌握从结构识别、函数追踪到算法还原的完整逆向流程;③理解“绑定二进制”的完整性校验设计及其局限性;④实践编写IDAPython脚本自动化提取与解密敏感数据。; 阅读建议:此资源以实战案例驱动,不仅展示技术细节,更强调逆向思维与验证方法,建议读者结合IDA调试环境,逐步跟随文中步骤进行动态分析与算法验证,深入理解每一步的推理依据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值