【算法】GCJ2019 Cryptopangrams

本文介绍了解决GCJ2019Cryptopangrams问题的详细步骤,包括如何从加密的整数序列中恢复原始消息,通过找到连续三个不同字符的公共素数来解开bijection映射,最终还原明文。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

GCJ2019 Cryptopangrams

题目:

出题人随机选了26个不超过N的素数,按照从小到大排列,把每个素数分给起对应的字母,得到了一个由大写字母到这26个素数的一个bijection。然后出题人通过这个bijection对一段message加密,加密过程为
(1)把每个字符替换成其对应的素数,得到一个长度和message相同的整数数列,计为v[]
(2)生成一个长度为n-1的数字序列a[],其中a[i] = v[i] * v[i+1]
保证26字符每个都至少在message里出现一次。n不超过100,告诉你数列a[],让你求出原文。
输入项:
N:素数最大值
L:密文个数
ARR:密文的数组
限制:
01 ≤ N ≤ 10100.
内存: 1 GB.
25 ≤ L ≤ 100.

解题思路

  • 由于每个字符至少出现一次,所以一定有连续的三个字符不相同的情况(ABC),这样a[i]=AB 和 a[i+1]=BC,可以求出两者的公约数B
  • 知道了其中一个素数B,即可向前后两个方向求出剩余素数
  • 将素数排序,可映射字母
  • 便利未排序的素数数组,并替换成映射的字母,即可得到明文

实现代码

代码写的比较粗糙,没有简略优化,待修正,不过逻辑没有问题

// 提供密文输入
class DataBuilder: NSObject {
    class func cryptopangramsData() -> [String:Any]{
//        the result should be SUBDERMATOGLYPHICFJKNQVWXZ
        return [
            "N":10000,
            "L":25,
            "ARR":[3292937,175597,18779,50429,375469,1651121,2102,3722,2376497,611683,489059,2328901,3150061,829981,421301,76409,38477,291931,730241,959821,1664197,3057407,4267589,4729181,5335543]
        ];
    }
}
//提供解密方法
class AlgorithmManager: NSObject {
    
    /// 解密方法
    /// - Parameter N: 质数最大值
    /// - Parameter L: 密文长度
    /// - Parameter ARR: 密文数组
    class func cryptopangrams(N:NSInteger, L:NSInteger, ARR:[NSInteger]) -> Any{
        var firstNum = -1
        var secondNum = -1
        var index = 0
        //遍历密文,找出两个不相等的密文,即代表着连续的三个不相同字符
        for i in 0 ..< L{
            if ARR[i] != ARR[i+1] {
                firstNum = ARR[i]
                secondNum = ARR[i+1]
                index = i
                break
            }
        }
        //如果没有说明密文有误 返回
        if firstNum < 0 || secondNum < 0 {
            return -1
        }
        //找到公约数
        let fac = self.findMaxCommonFactor(num1: firstNum, num2: secondNum)
        //向前求出质数
        let preArr = self.unlockNumArr(from: 0, to: index, fac: fac, arr: ARR, isReversed: true).reversed()
        //向后求出质数
        let nextArr = self.unlockNumArr(from: index+1, to: L-1, fac: fac, arr: ARR, isReversed: false)
        //整合质数数组
        var cryArrM = [NSInteger]()
        cryArrM.append(contentsOf: preArr)
        cryArrM.append(fac)
        cryArrM.append(contentsOf: nextArr)
        //将质数数组映射成字符串
        let cryStr = self.decriptCryptopangrams(cryArr: cryArrM)
        //返回字符串
        return cryStr
    }
    
    /// 求两个数值的最大公约数
    /// - Parameter num1: 第一个数
    /// - Parameter num2: 第二个数
    class func findMaxCommonFactor(num1:NSInteger, num2:NSInteger) -> NSInteger{
        var lNum = num1 > num2 ? num1 : num2
        var sNum = num1 < num2 ? num1 : num2
        var rNum = lNum % sNum
        //依据的是:被除数A与除数B得出余数C,余数C与除数B的公约数,和被除数A与除数B的公约数相同,所以反复利用这个理论,直到余数C为零时,除数B即为公约数
        while rNum != 0 {
            lNum = sNum
            sNum = rNum
            rNum = lNum % sNum
        }
        return sNum
    }
    
    /// 依据现有质数求出前后的质数
    /// - Parameter from: 开始的序号
    /// - Parameter to: 结束的序号
    /// - Parameter fac: 现有质数
    /// - Parameter arr: 密文数组
    /// - Parameter isReversed: 是否逆序
    class func unlockNumArr(from:NSInteger, to:NSInteger, fac:NSInteger, arr:[NSInteger], isReversed:Bool) -> [NSInteger]{
        //初始化质数数组
        var result = [NSInteger]()
        var facVar = fac
        if isReversed {
            //向前求质数时需要逆序
            for i in (from ... to).reversed() {
                let num = arr[i] / facVar
                result.append(num)
                facVar = num
            }
        }else{
            for i in from ... to {
                let num = arr[i] / facVar
                result.append(num)
                facVar = num
            }
        }
        return result
    }
    
    /// 将质数数组映射为字符串
    /// - Parameter cryArr: 质数数组
    class func decriptCryptopangrams(cryArr:[NSInteger]) -> String{
        var cryArrM = [NSInteger]()
        //先排序质数数组
        for num in cryArr {
            if cryArrM.count > 0 {
                for i in (0 ... cryArrM.count-1).reversed()  {
                    if cryArrM[i] < num {
                        cryArrM.insert(num, at: i+1)
                        break
                    }else if cryArrM[i] == num {
                        break
                    }
                    if i == 0 {
                        cryArrM.insert(num, at: 0)
                    }
                }
                //如果已经有26个了,说明已经全部映射完了,跳出循环
                if cryArrM.count >= 26 {
                    break
                }
            }else{
                cryArrM.append(num)
            }
        }
        let charArr :[String] = ["A","B","C","D","E","F","G","H","I","G","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
        var map = [String:String]()
        //将质数与字母进行映射
        for i in 0 ..< cryArrM.count {
            let num = cryArrM[i]
            map[String(num)] =  charArr[i]
        }
        var result = ""
        //依据映射将质数数组替换成字母
        for num in cryArr {
            result += map[String(num)]!
        }
        return result
    }
}
        //获取输入数据
        let data = DataBuilder.cryptopangramsData()
        //获取解密字符串
        let result = AlgorithmManager.cryptopangrams(N: data["N"] as! NSInteger, L: data["L"] as! NSInteger, ARR: data["ARR"] as! [NSInteger])
        //打印字符串
        print("result is "+(result as! String))

运行结果:
在这里插入图片描述
代码地址:https://github.com/sinianshou/EGSwiftLearning

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值