POJ 2159 Ancient Cipher(字符串排列替换)

原题地址:

http://poj.org/problem?id=2159

题意:古罗马采用一种混合替换、排列的方法对明文加密(替换即将一个字符替换为另外一种字符,不同字符替换后的字符各不相同;排列即打乱字符的先后顺序),给定一串密文和一串明文,判断这串密文是否可能表示这串明文。

解题思路

题目中只是解释了替换和排列的定义,但没有给出具体的替换规则和排列规则,因此这两种规则执行的先后顺序,以及执行的规则都是未知的。
自己的第一直觉是对两个字符串排序,对应字符出现是否存在矛盾,后来发现想得过于简单,比如排序后的AABBCEF和BCCGGHJ是可以变换的。

下面对两种变换分析:
1. 排列:不管怎么排列字符,都不会改变字符的出现情况(频次),单纯只是打乱了顺序,所以只要两个字符串的字符出现情况相同,就可能是明文密文对。即排列对本题的判定没有影响
2. 替换:题目中“Substitutes for all letters must be different. ”这句话非常关键!说明这26个字母对应的替换规则不能有重叠(如A->X同时B->X是不允许的,不然怎么解密?当然一个A->X同时A->Y也不合理),因此明密文字符之间存在一对一的映射关系。

假设A->x,B->y,C->z,那么ABBACA->xyyxzx,虽然xyz未知,但由于上述的一对一规则,明文中A出现多少次,密文中x也将出现多少次(即使被打乱),明文中B出现多少次,密文y也将出现多少次。
根据这种对应字符频次分布一致的规律,我们只需记录下密文中每个字符的频次freq1[i],记录下明文中每个字符的频次freq[2], 将它们都降序排列(升序亦可),比较这些频次是否全部相同即可。

例:ABBACA->xyyxzx,那么freq1= freq2 = {3,2,1};但是如果出现ABAACA->xyyxzx,则freq1={4,1,1},freq2 = {3,2,1},则密文不能由明文得到。

AC代码如下

#include <iostream>
#include <cstring>
#include <algorithm>
#define MAXLEN 105
using namespace std;

bool cmp(int a, int b) //降序
{
    return b>a;
}

int main()
{
    char str1[MAXLEN], str2[MAXLEN];
    cin >> str1 >> str2;
    int len1 = strlen(str1), len2 = strlen(str2);
    if (len1 != len2) //长度不一则直接否定
    {
        cout << "NO" << endl;
        return 0;
    }
    //freq1记录密文中每个字符的出现次数,freq2记录明文中每个字符的出现次数
    int freq1[27],freq2[27], i;
    for (i = 0; i<27; ++i) //初始化
        freq1[i] = freq2[i] = 0;
    for (i = 0; i<strlen(str1); ++i)
    {
        freq1[str1[i]-'A']++;
        freq2[str2[i]-'A']++;
    }
    //降序排列
    sort(freq1, freq1+27,cmp);
    sort(freq2, freq2+27,cmp);
    for (i = 0; i<27; ++i)
        if(freq1[i] != freq2[i]) //出现某个未知字符频次不相等的情况
            break;
    if (i == 27)
        cout << "YES" << endl; //全部频次都相同
    else
        cout << "NO" << endl;
    return 0;
}

内存占用: 700Kb 耗时:0ms
算法复杂度:O(nlogn)

posted @ 2017-03-27 18:07  Lecholin  阅读(98)  评论(0编辑  收藏  举报