算法| ss 字符串数组对象

本文介绍了多个编程问题的解决方案,包括将罗马数字转换为整数、查找最长回文串、字符串相加与相乘、寻找最长和谐子序列、计算连续序列长度、找出最大数、最小操作次数使数组元素相等、前K个高频单词以及重复字符串匹配和字符串排列检查。
  • 13.罗马数字转整数
  • 409.最长回文串
  • 415.字符串相加
  • 594.最长和谐子序列
  • 43.字符串相乘
  • 128.最长连续序列
  • 179.最大数
  • 462.最小操作次数使数组元素相等 II
  • 692.前K个高频单词
  • 686.重复叠加字符串匹配
  • 567.字符串的排列

13. 罗马数字转整数

/**
 * @param {string} s
 * @return {number}
 */
// 构建map匹配值,增加带IV这种的
// for遍历循环,
// 1.先看匹配是否满足2个字符的 i+2
// 2. 单个字符匹配
var romanToInt = function (s) {
  const map = {
    I: 1,
    IV: 4,
    V: 5,
    IX: 9,
    X: 10,
    XL: 40,
    L: 50,
    XC: 90,
    C: 100,
    CD: 400,
    D: 500,
    CM: 900,
    M: 1000,
  };
  let ans = 0;
  for (let i = 0; i < s.length; i++) {
    const substr = s.substring(i, i + 2);
    if (i + 1 < s.length && map[substr]) {
      ans += map[substr];
      i += 1;
    } else {
      ans += map[s[i]];
    }
  }
  //   console.log(ans);
  return ans;
};
// romanToInt("III");
// romanToInt("IV");
romanToInt("MCMXCIV");
// 示例 1:
// 输入: s = "III"
// 输出: 3
// 示例 2:
// 输入: s = "IV"
// 输出: 4

409.最长回文串

/**
 * @param {string} s
 * @return {number}
 */
// 思路
// 统计各个字符出现的次数
// 统计奇数个数, 只留一个奇数个数,其他个数删除1
// 如果奇数个数大于1
// 总长度= 字符串长度- (奇数个数-1)
//
var longestPalindrome = function (s) {
  const map = {};
  for (let ch of s) {
    map[ch] = (map[ch] ?? 0) + 1;
  }
  let deleteCount = 0;
  for (let key in map) {
    if (map[key] % 2 !== 0) {
      deleteCount += 1;
    }
  }
  const ans = deleteCount > 1 ? s.length - (deleteCount - 1) : s.length;
  //   console.log(ans);
  return ans;
};
longestPalindrome("abccccdd");

// 输入:s = "abccccdd"
// 输出:7

415. 字符串相加

/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
// 思路
// 从两个数的末尾进行取值 相加
// while循环很重要, i>=0 j>=0  carry>=0
// 往数组里添加 求余值放入数组  进位值进入下一轮计算
var addStrings = function (num1, num2) {
  let i = num1.length - 1;
  let j = num2.length - 1;
  let carry = 0;
  const res = [];
  while (i >= 0 || j >= 0 || carry !== 0) {
    let c1 = i >= 0 ? num1.charAt(i) - "0" : 0;
    let c2 = j >= 0 ? num2.charAt(j) - "0" : 0;
    // console.log("c1", c1);
    // console.log("c2", c2);
    const sum = c1 + c2 + carry;
    res.push(sum % 10);
    carry = Math.floor(sum / 10);
    i--;
    j--;
  }
  //   console.log(res);
  return res.reverse().join("");
};
addStrings("11", "123");
// 输入:num1 = "11", num2 = "123"
// 输出:"134"

594.最长和谐子序列

/**
 * @param {number[]} nums
 * @return {number}
 */
// 思路
// 统计各个数出现的次数
// 利用map 来获取最大值
// map是有序的, 不想对象是无序的
var findLHS = function (nums) {
  const map = new Map();
  for (let ch of nums) {
    map.set(ch, (map.get(ch) ?? 0) + 1);
  }
  console.log(map);
  let max = 0;
  for (let key of map.keys()) {
    if (map.get(key + 1)) {
      max = Math.max(max, map.get(key) + map.get(key + 1));
    }
  }
  console.log(max);
  return max;
};
findLHS([1, 3, 2, 2, 5, 2, 3, 7]);
// 输入:nums = [1,3,2,2,5,2,3,7]
// 输出:5

43. 字符串相乘

/**
 * @param {string} num1
 * @param {string} num2
 * @return {string}
 */
// 思路
// 两个数相乘 结果的长度不会超过两个数长度的和 9*9 = 81
// 从结尾开始相乘 res[i + j + 1] 为 i j的位置
// res[i + j + 1] 求余
// res[i + j] 存进位
var multiply = function (num1, num2) {
  const len1 = num1.length;
  const len2 = num2.length;
  const res = Array(len1 + len2).fill(0);
  for (let i = len1 - 1; i >= 0; i--) {
    const n1 = +num1[i];
    for (let j = len2 - 1; j >= 0; j--) {
      const n2 = +num2[j];
      const multi = n1 * n2;
      const sum = res[i + j + 1] + multi;
      res[i + j + 1] = sum % 10;
      res[i + j] += (sum / 10) | 0;
    }
  }
  while (res[0] === 0) {
    res.shift();
  }
  let ans = res.length ? res.join("") : "0";
  console.log(ans);
  return ans;
};
multiply("2", "3");
multiply("123", "456");

128. 最长连续序列

/**
 * @param {number[]} nums
 * @return {number}
 */
// 思路
// 数组排序
// 边界数组1个的情况
// for循环遍历 初始为1
// 如果当前和前一个数相等 则continue
// 如果当前比前一个数大1,temp++
// 否则 temp置为初始1
// Math.max更新结果
var longestConsecutive = function (nums) {
  if (nums.length === 0) return 0;
  nums.sort((a, b) => a - b);
  let ans = 1;
  let temp = 1;
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] === nums[i - 1]) continue;
    if (nums[i] - nums[i - 1] === 1) {
      temp += 1;
    } else {
      temp = 1;
    }
    ans = Math.max(ans, temp);
  }
  console.log(ans);
  return ans;
};
longestConsecutive([100, 4, 200, 1, 3, 2, 2]);
longestConsecutive([0, 3, 7, 2, 5, 8, 4, 6, 0, 1]);
// 输入:nums = [100,4,200,1,3,2]
// 输出:4

179. 最大数

/**
 * @param {number[]} nums
 * @return {string}
 */
// 思路
// 字符串排序 两个数转string进行拼接比较
var largestNumber = function (nums) {
  nums.sort((a, b) => {
    let stra = b.toString() + a.toString();
    let strb = a.toString() + b.toString();
    console.log(stra, strb);
    return stra > strb ? 1 : -1;
  });
  if (nums[0] === 0) return "0";
  console.log(nums.join(""));
  return nums.join("");
};
largestNumber([3, 30, 34, 5, 9]);
// 输入:nums = [3,30,34,5,9]
// 输出:"9534330"

462. 最小操作次数使数组元素相等 II

/**
 * @param {number[]} nums
 * @return {number}
 */
// 思路
// 数组排序 找到中间值val
// for遍历数组, 取各个数和中间值的绝对值差之和
var minMoves2 = function (nums) {
  nums.sort((a, b) => a - b);
  const mid = Math.floor(nums.length / 2);
  const midValue = nums[mid];
  let result = 0;
  for (let ch of nums) {
    result += Math.abs(ch - midValue);
  }
  console.log(result);
  return result;
};
minMoves2([1, 2, 3]);

// 输入:nums = [1,2,3]
// 输出:2
// 解释:
// 只需要两次操作(每次操作指南使一个元素加 1 或减 1):
// [1,2,3]  =>  [2,2,3]  =>  [2,2,2]

692.前K个高频单词

/**
 * @param {string[]} words
 * @param {number} k
 * @return {string[]}
 */
// 思路
// 哈希统计+ sort排序
var topKFrequent = function (words, k) {
  const map = {};
  for (let ch of words) {
    map[ch] = (map[ch] ?? 0) + 1;
  }
  let ans = Object.keys(map).sort((a, b) =>
    map[a] === map[b] ? (a > b ? 1 : -1) : map[b] - map[a]
  );
  //   console.log(ans.slice(0, k));
  return ans.slice(0, k);
};
topKFrequent(["i", "love", "leetcode", "i", "love", "coding"], 2);

// 输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
// 输出: ["i", "love"]
// 解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
//     注意,按字母顺序 "i" 在 "love" 之前。

686.重复叠加字符串匹配

/**
 * @param {string} a
 * @param {string} b
 * @return {number}
 */
// 思路
// 把a至少拼接成b的长度 判断有没有包含b
// 如果不包含再拼接一次(可能是头尾的关系),判断是否包含, 包含更新结果,不包含返回-1
var repeatedStringMatch = function (a, b) {
  if (!b.length) return 0;
  let targetStr = a;
  let len = b.length;
  let result = 1;
  while (targetStr.length < len) {
    targetStr += a;
    result += 1;
  }
  if (targetStr.includes(b)) {
    return result;
  }

  targetStr += a;
  result += 1;
  if (targetStr.includes(b)) {
    return result;
  } else {
    return -1;
  }
};
console.log(repeatedStringMatch("abcd", "cdabcdab"));

567.字符串的排列

/**
 * @param {string} s1
 * @param {string} s2
 * @return {boolean}
 */
// 思路 固定滑窗 + 哈希统计
// 这里也只有2个元素好解决, 两个指针一一对应, 如果s1 数量比2大 ,就不好搞了
//   left 和right 相隔2个元素
//   right的作用是消掉 left的作用是补上
var checkInclusion = function (s1, s2) {
  const map = {};
  for (let ch of s1) {
    map[ch] = (map[ch] ?? 0) + 1;
  }
  let k = s1.length;
  let right = 0;
  let left = 0;
  while (right < k) {
    let ch = s2[right];
    if (map[ch]) map[ch]--;
    right++;
  }
  if (Object.values(map).every((item) => item <= 0)) return true;
  //   left 和right 相隔2个元素
  //   right的作用是消掉 left的作用是补上
  for (let right = k; right < s2.length; right++) {
    let leftCh = s2[left];
    let rightCh = s2[right];
    if (map[leftCh] != undefined) map[leftCh] += 1;
    if (map[rightCh] != undefined) map[rightCh] -= 1;
    // console.log("left-right", left, right);
    left += 1;
    if (Object.values(map).every((item) => item <= 0)) return true;
  }
  return false;
};
console.log(checkInclusion("ab", "eidbaooo"));
console.log(checkInclusion("ab", "aidboaoo"));
// 输入:s1 = "ab" s2 = "eidbaooo"
// 输出:true
// 解释:s2 包含 s1 的排列之一 ("ba").
// 示例 2:

// 输入:s1= "ab" s2 = "eidboaoo"
// 输出:false

// left-right 0 2
// left-right 1 3
// left-right 2 4
// true
// left-right 0 2
// left-right 1 3
// left-right 2 4
// left-right 3 5
// left-right 4 6
// left-right 5 7
// false
<think>嗯,用户现在想在C++中根据字符串字符串数组中的位置抛出异常。这个问题需要仔细分析,因为涉及到数组索引、异常处理以及可能的字符串操作。首先,我需要回顾一下用户之前的提问和我的回答,看看有没有相关的信息可以参考。 用户之前的问题是关于如何在C++中获取某个值在数组中的位置,我给出了几种方法,包括遍历、STL算法、指针运算和二分查找。现在用户的需求转变为根据位置抛出异常,这可能意味着当访问数组的某个位置时,如果位置无效,需要抛出异常。 接下来,用户引用了三个站内的引用,不过看起来这些引用主要是关于C语言字符串赋值给指针、数组遍历和Python中的元组与列表的区别。这些引用可能与当前问题关联不大,但用户特别提到要参考上一次的问题和回答,所以需要重点回顾之前的C++数组处理内容。 用户现在的问题是如何在C++中根据字符串数组的位置抛出异常。可能的场景是:当尝试访问数组的某个索引时,如果索引越界,抛出一个包含该位置字符串的异常信息。例如,访问索引5,但数组只有3个元素,此时抛出异常,提示索引5无效,并显示数组中的有效内容。 首先,需要明确几个步骤: 1. **检查索引的有效性**:在访问数组元素前,确认索引是否在有效范围内(0到size-1)。 2. **构造异常信息**:当索引无效时,需要生成包含位置信息和数组内容的错误消息。 3. **选择合适的异常类型**:C++标准库中有std::out_of_range异常,适合用于索引越界的情况。 4. **实现异常抛出**:在检测到无效索引时,抛出构造好的异常对象。 接下来,考虑如何实现这些步骤。对于字符串数组,可以使用std::vector<std::string>或者原生的数组。但为了更好的动态性和安全性,推荐使用std::vector。检查索引是否越界可以通过比较索引和数组的size()方法。构造异常信息时,可以使用std::stringstream来格式化错误信息,包含索引值和数组内容。 可能的实现方式包括: - 封装一个函数,接受数组和索引,返回对应的元素,如果索引无效则抛出异常。 - 在类中封装数组,重载operator[],并在其中加入范围检查,抛出异常。 用户可能需要具体的代码示例,所以需要提供两种实现方式:基础版和进阶版。基础版使用简单的函数封装,而进阶版可能使用类来封装数组,提供更安全的访问方式。 此外,还需要考虑自定义异常类型,虽然std::out_of_range已经足够,但用户可能需要更详细的信息,比如包含数组内容,这时候可以自定义异常类继承自std::exception,并重载what()方法。 另外,用户可能在处理字符串数组时,希望将数组的内容也包含在异常信息中,以便调试。例如,当索引超出范围时,异常信息不仅指出无效的索引,还列出数组当前的元素,帮助定位问题。 在实现过程中,需要注意字符串的拼接和格式化,避免在构造异常信息时出现性能问题,尤其是当数组很大时。不过对于异常处理来说,这种情况通常发生在错误发生时,性能不是首要考虑因素。 最后,需要测试代码示例,确保索引有效时返回正确元素,无效时抛出正确的异常,并且异常信息准确。 总结步骤: 1. 检查索引是否在有效范围内。 2. 如果无效,构造包含索引和数组内容的错误信息。 3. 抛出std::out_of_range异常或自定义异常。 4. 提供使用示例,包括正确和错误的情况。 可能还需要考虑如何将数组内容格式化为字符串,例如使用循环将每个元素拼接成逗号分隔的列表,并包含在异常信息中。</think>根据您提出的需求,在C++中实现基于字符串数组位置抛出异常的功能,可通过以下两种典型方法实现: ### 一、基础实现版(直接索引验证) ```cpp #include <stdexcept> #include <vector> #include <sstream> // 方法1:直接访问验证 std::string getStringAt(const std::vector<std::string>& arr, size_t index) { if(index >= arr.size()) { std::stringstream ss; ss << "索引越界:位置 " << index << " 超出数组范围(0-" << arr.size()-1 << ")"; throw std::out_of_range(ss.str()); } return arr[index]; } // 方法2:带数组内容反馈 std::string getStringWithContext(const std::vector<std::string>& arr, size_t index) { if(index >= arr.size()) { std::stringstream ss; ss << "非法访问位置:" << index << "\n当前数组内容:"; for(const auto& s : arr) ss << "'" << s << "' "; ss << "\n最大允许索引:" << arr.size()-1; throw std::runtime_error(ss.str()); } return arr[index]; } ``` ### 二、进阶封装版(安全数组容器) ```cpp #include <algorithm> #include <iterator> class SafeStringArray { private: std::vector<std::string> data; public: explicit SafeStringArray(std::initializer_list<std::string> init) : data(init) {} // 重载下标运算符(只读) const std::string& operator[](size_t pos) const { validateIndex(pos); return data[pos]; } // 带异常信息的访问方法 std::string get(size_t pos) const { validateIndex(pos); return data[pos]; } // 获取数组副本用于异常信息 std::vector<std::string> getArraySnapshot() const { return data; } private: void validateIndex(size_t pos) const { if(pos >= data.size()) { std::stringstream ss; ss << "位置 " << pos << " 无效\n" << "数组长度:" << data.size() << "\n" << "存储内容:"; std::copy(data.begin(), data.end(), std::ostream_iterator<std::string>(ss, " ")); throw std::out_of_range(ss.str()); } } }; ``` ### 三、使用示例 ```cpp int main() { // 基础版使用 std::vector<std::string> colors{"红","绿","蓝"}; try { std::cout << getStringAt(colors, 5); // 触发异常 } catch(const std::exception& e) { std::cerr << "错误捕获:\n" << e.what(); } // 进阶版使用 SafeStringArray fruits({"苹果", "香蕉", "橙子"}); try { std::cout << fruits.get(3); // 触发异常 } catch(const std::out_of_range& e) { std::cerr << "安全数组异常:\n" << e.what(); } } ``` ### 四、异常信息优化建议表 | 优化方向 | 实现方法 | 优点 | |----------------|-----------------------------------|--------------------------| | 精准定位 | 包含文件名和行号信息 | 快速定位错误发生位置 | | 上下文存储 | 在异常对象中保存数组快照 | 事后分析时可复现现场数据 | |语言支持 | 根据系统语言切换异常信息语言 | 提升国际化支持 | | 堆栈追踪 | 集成boost::stacktrace库 | 显示完整调用链 | ### 五、自定义异常类型 ```cpp class ArrayAccessException : public std::exception { std::string msg; size_t invalid_index; std::vector<std::string> snapshot; public: ArrayAccessException(size_t idx, std::vector<std::string> arr) : invalid_index(idx), snapshot(std::move(arr)) { std::stringstream ss; ss << "访问异常:索引 " << idx << "\n数组快照(大小=" << snapshot.size() << "):"; for(const auto& s : snapshot) ss << s << " "; msg = ss.str(); } const char* what() const noexcept override { return msg.c_str(); } size_t getInvalidIndex() const { return invalid_index; } std::vector<std::string> getSnapshot() const { return snapshot; } }; ``` ### 六、性能优化技巧 1. **异常对象轻量化**:避免在异常构造时进行深拷贝 ```cpp // 使用共享指针存储快照数据 using SnapShotPtr = std::shared_ptr<const std::vector<std::string>>; class LightweightException : public std::exception { SnapShotPtr snapshot; // 共享数据指针 // ... }; ``` 2. **预分配异常信息**:对高频访问的数组可提前缓存元数据 ```cpp void validateIndex(size_t pos) const { thread_local static std::string cached_info; if(pos >= data.size()) { if(cached_info.empty()) { // 首次异常时生成元数据 std::stringstream ss; ss << "数组固定长度:" << data.size(); cached_info = ss.str(); } throw std::out_of_range(cached_info + " 请求位置:" + std::to_string(pos)); } } ``` --- ### 相关问题 1. 如何捕获并处理不同类型的数组访问异常? 2. 在抛出异常时如何避免内存泄漏? 3. 如何实现跨线程安全的数组异常处理机制? 4. 怎样通过编译时检查预防数组越界访问? [^1]: 字符串常量存储特性 [^2]: 数组遍历基础原理 [^3]: 数据结构安全访问规范
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值