#P2386. 第2题-解密
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 570
            Accepted: 152
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2023年5月10日-暑期实习
                              
                      
          
 
- 
                        算法标签>暴力枚举          
 
第2题-解密
题解思路
本题要求从一个由数字组成的字符串 M 中找到一个子串,与一个给定的数字 N 进行运算(加、减、乘),使得运算结果符合以下条件:
- 结果的所有数位相同:即运算结果的每一位数都相同(例如 
4444、77777等)。 - 选择最大值:若有多个符合条件的子串,则选择运算结果最大的一个子串作为最终答案。
 
运算规则要求子串长度在 3 到 12 之间,并且在乘法运算的情况下,子串最多 3 位,以防止结果过大。
详细步骤
- 
遍历子串:由于密码串的长度限制在
3到12之间,因此我们可以枚举从M中提取不同长度的子串。以长度从12到3的顺序枚举,可以在找到符合要求的结果后提前结束遍历。 - 
计算运算结果:
- 对每一个提取出的子串,根据运算符 
k(加、减、乘),与密钥数字N进行计算,得到运算结果。 - 运算结果需要满足所有位数相同。为此,检查结果的各位数,判断其是否符合条件。
 
 - 对每一个提取出的子串,根据运算符 
 - 
结果筛选:
- 如果运算结果的各位数相同且比当前最大值大,则更新最终结果。
 - 若有多个符合条件的子串,则选择其中运算结果最大的子串。
 
 - 
终止条件:如果在当前长度的子串中找到满足要求的最大值,则直接输出结果。否则,继续尝试较短的子串,直到找到符合条件的结果。
 
复杂度分析
- 时间复杂度:对于字符串长度 
N,子串的长度上限为12,因此枚举子串的复杂度约为O(N * 12)。对于每个子串,进行一次运算和检测符合条件的操作,时间复杂度为常数级。因此,整体时间复杂度为O(N)。 - 空间复杂度:仅需常数空间存储运算结果和遍历子串,不涉及额外的复杂数据结构,因此空间复杂度为 
O(1)。 
代码实现
Python代码
def is_uniform(num):
    """检查一个数字的所有位是否相同"""
    if num < 0:
        return False  # 负数不符合条件
    num_str = str(num)
    return all(digit == num_str[0] for digit in num_str)
def main():
    import sys
    # 读取输入
    M = sys.stdin.readline().strip()
    N_str = sys.stdin.readline().strip()
    k = sys.stdin.readline().strip()
    # 尝试将 N 转换为整数,并进行基本验证
    try:
        N = int(N_str)
    except ValueError:
        print(-1)
        return
    # 检查 N 是否在有效范围内
    if N < 0 or N > 9999999999:
        print(-1)
        return
    # 如果运算符是乘法,检查 N 是否不超过 999
    if k == '*':
        if N > 999:
            print(-1)
            return
    max_result = -1
    target_password = -1
    # 遍历子串长度从3到12
    for length in range(3, min(len(M), 12) + 1):
        for i in range(len(M) - length + 1):
            substring = M[i:i + length]
            
            # 跳过以0开头且长度大于1的子串
            if substring[0] == '0' and length > 1:
                continue
            x = int(substring)
            # 执行运算
            if k == '+':
                result = x + N
            elif k == '-':
                result = x - N
                if result < 0:
                    continue  # 结果为负数,不符合条件
            elif k == '*':
                result = x * N
            else:
                continue  # 无效的运算符,跳过
            # 检查结果是否所有位相同
            if is_uniform(result):
                if result > max_result:
                    max_result = result
                    target_password = substring
    # 输出结果
    print(target_password if target_password != -1 else -1)
if __name__ == "__main__":
    main()
C++代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
// 检查一个数字的所有位是否相同
bool is_uniform(long long num) {
    if (num < 0) return false; // 负数不符合条件
    string num_str = to_string(num);
    char first_digit = num_str[0];
    for(char c : num_str){
        if(c != first_digit){
            return false;
        }
    }
    return true;
}
int main(){
    string M;
    long long N;
    char k;
    cin >> M >> N >> k;
    // 检查秘钥数字 N 是否在有效范围内
    if(N < 0 || N > 9999999999){
        cout << "-1" << endl;
        return 0;
    }
    // 如果运算符是乘法,检查 N 是否不超过 999
    if(k == '*' && N > 999){
        cout << "-1" << endl;
        return 0;
    }
    string target_password = "-1"; // 默认输出为 -1
    long long max_result = -1; // 初始化最大结果为 -1
    int len = M.length();
    // 遍历所有可能的子串长度,从3到12
    for(int length = 3; length <= min(12, len); length++){
        for(int i = 0; i <= len - length; i++){
            string substring = M.substr(i, length);
            
            // 跳过以 '0' 开头且长度大于1的子串
            if(substring[0] == '0' && length > 1){
                continue;
            }
            long long x;
            try{
                x = stoll(substring);
            }
            catch(...){
                continue; // 如果转换失败,跳过该子串
            }
            long long result;
            if(k == '+'){
                result = x + N;
            }
            else if(k == '-'){
                result = x - N;
                if(result < 0){
                    continue; // 结果为负数,不符合条件
                }
            }
            else if(k == '*'){
                result = x * N;
            }
            else{
                continue; // 无效的运算符,跳过
            }
            // 检查运算结果是否所有位相同
            if(is_uniform(result)){
                if(result > max_result){
                    max_result = result;
                    target_password = substring;
                }
            }
        }
    }
    cout << target_password << endl; // 输出结果
    return 0;
}
Java代码
import java.util.Scanner;
public class Main {
    /**
     * 检查一个数字的所有位是否相同。
     * @param num 要检查的数字。
     * @return 如果所有位相同且非负,返回 true;否则返回 false。
     */
    public static boolean isUniform(long num) {
        if (num < 0) return false; // 负数不符合条件
        String numStr = Long.toString(num);
        char firstDigit = numStr.charAt(0);
        for(int i = 1; i < numStr.length(); i++) {
            if(numStr.charAt(i) != firstDigit){
                return false;
            }
        }
        return true;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String M = scanner.next();
        long N;
        try {
            N = scanner.nextLong();
        } catch(Exception e){
            System.out.println("-1");
            return;
        }
        String kStr = scanner.next();
        if(kStr.length() == 0){
            System.out.println("-1");
            return;
        }
        char k = kStr.charAt(0);
        // 检查秘钥数字 N 是否在有效范围内
        if(N < 0 || N > 9999999999L){
            System.out.println("-1");
            return;
        }
        // 如果运算符是乘法,检查 N 是否不超过 999
        if(k == '*' && N > 999){
            System.out.println("-1");
            return;
        }
        long maxResult = -1;
        String targetPassword = "-1";
        int len = M.length();
        // 遍历所有可能的子串长度,从3到12
        for(int length = 3; length <= Math.min(12, len); length++) {
            for(int i = 0; i <= len - length; i++) {
                String substring = M.substring(i, i + length);
                // 跳过以 '0' 开头且长度大于1的子串
                if(substring.charAt(0) == '0' && length > 1){
                    continue;
                }
                long x;
                try {
                    x = Long.parseLong(substring);
                } catch(NumberFormatException e){
                    continue; // 如果转换失败,跳过该子串
                }
                long result;
                if(k == '+'){
                    result = x + N;
                }
                else if(k == '-'){
                    result = x - N;
                    if(result < 0){
                        continue; // 结果为负数,不符合条件
                    }
                }
                else if(k == '*'){
                    result = x * N;
                }
                else{
                    continue; // 无效的运算符,跳过
                }
                // 检查运算结果是否所有位相同
                if(isUniform(result)){
                    if(result > maxResult){
                        maxResult = result;
                        targetPassword = substring;
                    }
                }
            }
        }
        System.out.println(targetPassword);
    }
}
javascript
const readline = require("readline");
const rl = readline.createInterface({ input: process.stdin });
var iter = rl[Symbol.asyncIterator]();
const readLine = async () => (await iter.next()).value;
/**
 * 检查一个数字的所有位是否相同。
 * @param {number} num 要检查的数字。
 * @return {boolean} 如果所有位相同且非负,返回 true;否则返回 false。
 */
function isUniform(num) {
    if (num < 0) return false; // 负数不符合条件
    let numStr = num.toString();
    let firstDigit = numStr[0];
    return numStr.split("").every(digit => digit === firstDigit);
}
async function main() {
    let M = await readLine(); // 读取第一个字符串
    let N;
    
    try {
        N = BigInt(await readLine()); // 读取秘钥数字 N,使用 BigInt 以避免超出范围
    } catch (e) {
        console.log("-1");
        return;
    }
    let kStr = await readLine();
    if (kStr.length === 0) {
        console.log("-1");
        return;
    }
    let k = kStr[0]; // 获取运算符
    // 检查秘钥数字 N 是否在有效范围内
    if (N < 0 || N > 9999999999n) {
        console.log("-1");
        return;
    }
    // 如果运算符是乘法,检查 N 是否不超过 999
    if (k === '*' && N > 999n) {
        console.log("-1");
        return;
    }
    let maxResult = -1n;
    let targetPassword = "-1";
    let len = M.length;
    // 遍历所有可能的子串长度,从 3 到 12
    for (let length = 3; length <= Math.min(12, len); length++) {
        for (let i = 0; i <= len - length; i++) {
            let substring = M.substring(i, i + length);
            // 跳过以 '0' 开头且长度大于1的子串
            if (substring[0] === '0' && length > 1) {
                continue;
            }
            let x;
            try {
                x = BigInt(substring); // 使用 BigInt 处理大数
            } catch (e) {
                continue; // 如果转换失败,跳过该子串
            }
            let result;
            if (k === '+') {
                result = x + N;
            } else if (k === '-') {
                result = x - N;
                if (result < 0) {
                    continue; // 结果为负数,不符合条件
                }
            } else if (k === '*') {
                result = x * N;
            } else {
                continue; // 无效的运算符,跳过
            }
            // 检查运算结果是否所有位相同
            if (isUniform(result)) {
                if (result > maxResult) {
                    maxResult = result;
                    targetPassword = substring;
                }
            }
        }
    }
    console.log(targetPassword);
    rl.close();
}
// 运行主函数
main();
示例解释
对于输入示例:
- 
若
M = "6833023887793076998810418710",N = 2211,k = '-':- 符合条件的子串有 
8877和9988。9988 - 2211 = 7777是最大结果。 
 - 符合条件的子串有 
 - 
若
M = "68846787793076946788418710",N = 4210,k = '+':- 符合条件的子串为 
884678,884678 + 4210 = 888888是最大结果。 
 - 符合条件的子串为 
 
题目内容
在全球恐怖主义危机下,一组间谍团队接收到了来自地下工作者的一串神秘代码。这组代码可以帮助他们访问恐怖分子的服务器,但是他们需要先解密代码才能使用它。代码是由数字0 - 9 组成的字符串 M,而解密过程需要一个秘钥数字 N 和一个运算符 k (加减乘中的一个)。
解密过程分为三个步骤:
第一步,团队成员需要使用秘钥数字 N 对 M 进行一系列 k 运算,并尝试截取其中的一段数字 x。如果 x 和 N 的运算结果是一个所有位数相同的数字,那么这段数字就有可能是真正的密码。例如,如果 x 为 111,N 为 2,k 为乘法,那么计算结果是 111×2=222,满足条件,因此 111 就是所寻找的目标密码串之一。
第二步,如果存在多种满足第一点条件的情况,那么团队成员需要选择计算结果最大的一种作为真正的密码。
第三步,团队成员们需要在 M (M的长度不超过100) 中找到长度为 3 到 12 位的密码串,并尝试使用秘钥数字 N 和运算符 k (k 为 + 或 − 或 ∗的一种)进行解密。由于秘钥数字 N 可能非常大,因此需要确保 N 不大于 9999999999。另外,在乘法场景下,团队成员们约定乘数最大为 3 位数,以避免数据过于庞大。 如果没有找到符合条件的密码串,则输出 −1,表示密码串不存在。
输入描述
输入第一行为加密后的字符串M
输入第二行为密钥数字N
输入第三行为运算符k
输出
满足计算结果所有位数相同,且计算结果最大的值。
样例1
输入
6833023887793076998810418710
2211
-
输出
9988
解释:通过计算, 8877 - 2211= 6666 而 9988 - 2211 = 7777,因为7777 > 6666,则目标密码串为9988。
样例2
输入
68846787793076946788418710
4210
+
输出
884678
解释:通过计算,符合条件有两个,884678 + 4210 = 888888,4678 + 4210 = 8888。则目标密码串为884678。
样例3
输入
139804444677899222
2
*
输出
4444
解释:作为乘法场景,乘数最大值为 3 位数,本用例乘数为 2 。按要求,4444 * 2 = 8888, 222 * 2 = 444,均符合基本条件,从中选择结果最大值则目标密码串是4444。