【OD机试题解法笔记】字符串解密

题目描述

给定两个字符串string1和string2。
string1是一个被加扰的字符串。
string1由小写英文字母(‘a’-‘z’)和数字字符(‘0’-‘9’)组成,而加扰字符串由 ‘0’-‘9’、‘a’-‘f’ 组成。
string1里面可能包含0个或多个加扰子串,剩下可能有0个或多个有效子串,这些有效子串被加扰子串隔开。
string2是一个参考字符串,仅由小写英文字母(‘a’-‘z’)组成。
你需要在string1字符串里找到一个有效子串,这个有效子串要同时满足下面两个条件:
(1)这个有效子串里不同字母的数量不超过且最接近于string2里不同字母的数量,即小于或等于string2里不同字母的数量的同时且最大。
(2)这个有效子串是满足条件(1)里的所有子串(如果有多个的话)里字典序最大的一个。
如果没有找到合适条件的子串的话,请输出“Not Found”

输入描述
input_string1
input_string2

输出描述
output_string

用例1
输入

123admyffc79pt
ssyy

输出

pt

说明

将输入字符串 1 里的加扰子串 “123ad”、“ffc79” 去除后得到有效子串序列:“my”、“pt”,其中 “my” 里不同字母的数量为 2(有‘m’和‘y’两个不同字母),“pt” 里不同字母的数量为 2(有‘p’和‘t’两个不同字母);输入字符串 2 里不同字母的数量为 2(有‘s’和‘y’两个不同字母)。
可得到最终输出结果为 “pt”,其不同字母的数量最接近与 “ssyy” 里不同字母的数量的同时字典序最大。

说明

用例2
输入

123admyffc79ptaagghi2222smeersst88mnrt
ssyyfgh

输出

mnrt

说明

将输入字符串 1 里的加扰子串 “123ad”、“ffc79”、“aa”、“2222”、“ee”、“88” 去除后得到有效子串序列:“my”、“pt”、“gghi”、“sm”、“rsst”、“mnrt”;输入字符串 2 里不同字母的数量为 5(有 “s”、“y”、“f”、“g”、“h” 5 个不同字母)。可得到最终输出结果为 “mnrt”,其不同字母的数量为(4)最接近于 “ssyyfgh” 里不同字母的数量,其他有效子串不同字母的数量都小于 “mnrt”。

用例3
输入

abcmnq
rt

输出

Not Found

说明

将输入字符串 1 里的加扰子串 “abc” 去除后得到有效子串序列:“mnq”;输入字符串 2 里不同字母的数量为 2(有 “r”、“t” 两个不同的字母)。可得到最终的输出结果为 “Not Found”,没有符合要求的有效子串,因有效子串里的不同字母的数量(为 3),大于输入字符串 2 里的不同字母的数量。

思考

提取重点:(1)加扰字符范围 ‘0’-‘9’、‘a’-‘f’ ;(2)参考字符串 string2 只包含小写字母;(3)string1 是有效子串与加扰字符的混合体,有效子串和加扰子串数量都可能为0;(4)要算出 string2 中不同字母的数量 N,然后在 string1 中找到不同字母数量不超过且最接近 N的有效子串集合,从集合中挑出字典序最大的那个,如果没有则输出"Not Found"。
解题思路:那么第一步先计算 string2 中不同字母的数量 N,这个用哈希集合 Set 统计;第二步,遍历 string1 字符列表,拼接有效子串,存入有效子串集合中。遍历 string1 把每个加扰字符替换成空格“ ”,然后 string1.split(" ")切分可用得到所有有效子串的集合,这个集合中会包含之前切割空格产生的空串项目,过滤掉空串就得到有效子串集合。这些逻辑考察了子串基本操作,如 split、replace等。如果记得住正则表达式语法也可用正则表达式快速获取有效子串。如果前面几种字符串操作都嫌弃,那么用双指针从 string1 中截取所有有效子串也可行,但我觉得写起来容易错,不熟练可能会花费许多时间调试!得到了有效子串集合,暂且命名为 effectingSubs,统计每个子串中不同字母的数量 M,保留 M 最大的子串,再按字典序倒序排序,返回第一个。如果结果集合为空返回 “Not Found”。

算法过程

  1. 读取输入:从标准输入读取 string1string2
  2. 计算 string2 的不同字母数量:使用 countDiff 函数计算 string2 中不同字母的数量。
  3. 提取有效子串
    • 定义加扰字符为 '0'-'9''a'-'f'
    • string1 中的所有加扰字符替换为空格。
    • 使用 split(' ') 分割字符串,得到有效子串列表。
    • 过滤掉空字符串。
  4. 筛选有效子串
    • 遍历所有有效子串,计算每个子串的不同字母数量。
    • 保留那些不同字母数量不超过 string2 的不同字母数量且尽可能接近的子串。
  5. 选择结果
    • 如果没有满足条件的子串,输出 “Not Found”。
    • 否则,按字典序降序排序,输出第一个子串。

参考代码1

function solution() {
  let str1 = readline();
  const str2 = readline();
  
  const strDiffNum = countDiff(str2);

  let badChars = 'abcdef';
  for (let i = 0; i <= 9; i++) {
    badChars += i+'';
  }
  for (let i = 0; i < str1.length; i++) {
    if (badChars.includes(str1[i])) {
      str1 = str1.replace(str1[i], " ")
    }
  }
  let arr1 = str1.split(' ');
  let effectingSubs = [];
  for (let s of arr1) {
    if (s !== '') {
      effectingSubs.push(s);
    }
  }

  // console.log(effectingSubs);

  let result = [];
  let maxDiffNum = 0;
  for (let s of effectingSubs) {
    let diffNum = countDiff(s);
    if (diffNum > 0 && diffNum <= strDiffNum) {
      if (diffNum === maxDiffNum) {
        maxDiffNum = Math.max(maxDiffNum, diffNum);
        result.push(s);
      } else if (diffNum > maxDiffNum) {
        maxDiffNum = diffNum;
        result = [s];
      }
    }
  }

  if (maxDiffNum === 0) {
    console.log("Not Found");
    return;

  }

  result.sort((a, b) => {
    if (a >= b) {
      return -1;
    } else {
      return 1;
    }
  });

  console.log(result[0]);
}


function countDiff(s) {
  const set = new Set();
  for (const c of s) {
    if (/[a-z]/.test(c)) { // 仅统计小写字母
      set.add(c);
    }
  }
  return set.size;
}

const cases = [
  `123admyffc79pt
  ssyy`, // pt
  `123admyffc79ptaagghi2222smeersst88mnrt
  ssyyfgh`,  // mnrt
  `abcmnq
  rt`,  // Not Found
];

let caseIndex = 0;
let lineIndex = 0;

const readline = (function () {
  let lines = [];
  return function () {
    if (lineIndex === 0) {
      lines = cases[caseIndex]
        .trim()
        .split("\n")
        .map((line) => line.trim());
    }
    return lines[lineIndex++];
  };
})();

cases.forEach((_, i) => {
  caseIndex = i;
  lineIndex = 0;
  solution();
});

潜在问题

  1. 加扰字符替换
    • 代码中使用 str1.replace(str1[i], " ") 逐个替换加扰字符为空格。这种方法会多次修改字符串,效率较低。
    • 更好的方法是使用正则表达式一次性替换所有加扰字符。
  2. 字典序排序
    • 使用 a >= b 作为比较函数不严谨,可能导致排序结果不符合预期。应该使用 localeCompare 或直接比较字符串。
参考代码2(优化版)
function solution() {
  let str1 = readline();
  const str2 = readline();
  
  const strDiffNum = countDiff(str2);

  // Replace all bad characters with spaces
  const badChars = /[0-9a-f]/g;
  str1 = str1.replace(badChars, ' ');
  
  // Split by spaces and filter out empty strings
  const effectingSubs = str1.split(' ').filter(s => s !== '');

  let result = [];
  let maxDiffNum = 0;
  for (const s of effectingSubs) {
    const diffNum = countDiff(s);
    if (diffNum > 0 && diffNum <= strDiffNum) {
      if (diffNum > maxDiffNum) {
        maxDiffNum = diffNum;
        result = [s];
      } else if (diffNum === maxDiffNum) {
        result.push(s);
      }
    }
  }

  if (result.length === 0) {
    console.log("Not Found");
    return;
  }

  // Sort in descending lexicographical order
  result.sort((a, b) => b.localeCompare(a));
  console.log(result[0]);
}

function countDiff(s) {
  const set = new Set(); // set自动去重
  for (const c of s) {
    if (/[a-z]/.test(c)) { // 仅统计小写字母
      set.add(c);
    }
  }
  return set.size;
}

const cases = [
  `123admyffc79pt
  ssyy`, // pt
  `123admyffc79ptaagghi2222smeersst88mnrt
  ssyyfgh`,  // mnrt
  `abcmnq
  rt`,  // Not Found
];

let caseIndex = 0;
let lineIndex = 0;

const readline = (function () {
  let lines = [];
  return function () {
    if (lineIndex === 0) {
      lines = cases[caseIndex]
        .trim()
        .split("\n")
        .map((line) => line.trim());
    }
    return lines[lineIndex++];
  };
})();

cases.forEach((_, i) => {
  caseIndex = i;
  lineIndex = 0;
  solution();
});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值