【OD机试题笔记】去除多余空格

题目

去除文本多余空格,但不去除配对单引号之间的多余空格。给出关键词的起始和结束下标,去除多余空格后刷新关键词的起始和结束下标。

条件约束:
在这里插入图片描述

  1. 不考虑关键词起始和结束位置为空格的场景;
  2. 单词的的开始和结束下标保证涵盖一个完整的单词,即一个坐标对开始和结束下标之间不会有多余的空格;
  3. 如果有单引号,则用例保证单引号成对出现;
  4. 关键词可能会重复;
  5. 文本字符长度 lengthlengthlength 取值范围:[0,100000][0, 100000][0,100000];

输入
输入为两行字符串:
第一行:待去除多余空格的文本,用例保证如果有单引号,则单引号成对出现,且单引号可能有多对。
第二行:关键词的开始和结束坐标,关键词间以逗号区分,关键词内的开始和结束位置以单空格区分。
例如:

Life is painting a  picture, not doing 'a  sum'.
8 15,20 26,43 45

关键单词为:painting picture sum

输出
输出为两行字符串:
第一行:去除多余空格后的文本
第二行:去除多余空格后的关键词的坐标开始和结束位置,为数组方式输出。

例如:

Life is painting a picture, not doing 'a  sum'.
[8, 15][19, 25][42, 44]

示例一
输入

Life is painting a  picture, not doing 'a sum'.
8 15,20 26,43 45

输出

Life is painting a picture, not doing 'a sum'.
[8, 15][19, 25][42, 44]

说明
apicture中间多余的空格进行删除
示例二
输入

Life is painting a  picture, not doing 'a  sum'.
8 15,20 26,43 45

输出

Life is painting a picture, not doing 'a  sum'.
[8, 15][19, 25][42, 44]

说明
asum之间有多余的空格,但是因为有成对单引号,不去除多余空格

思路

核心逻辑
  1. 状态标记:用布尔变量isInQuote标记是否在单引号内(遇'翻转状态),单引号内保留所有空格,外仅保留连续空格的第一个;
  2. 文本重构+偏移映射:遍历原文本构建新文本,用offsetMap记录「原下标→新下标」映射(仅添加字符时新下标currentIndex递增,多余空格映射到前一位置);
  3. 坐标更新:基于offsetMap将原始坐标转换为新坐标,按格式输出。
时空复杂度
  • 时间复杂度:O(n),仅需一次遍历文本(n为文本长度),坐标转换为O(m)(m为关键词数,m≤n),整体仍为O(n);
  • 空间复杂度:O(n),主要为新文本、偏移映射表的空间开销,可兼容10万字符规模。
关键避坑
  • 禁止实时修改坐标,需通过偏移映射统一转换;
  • 输出坐标无多余空格,严格匹配[x,y][x,y]格式;
  • 单引号状态仅需翻转,无需复杂区间记录(题目保证成对)。

代码

let data =`Life is painting a  picture, not doing 'a sum'.
8 15,20 26,43 45`;
data = data.split('\n');
let index = 0;
async function readline() {
    return data[index++];
}

void (async function () {
    let line;
    while ((line = await readline())) {
        const coordinates = (await readline()).split(',').map(item => item.split(' ').map(Number));

        if (!line) {
            console.log('');
            const emptyCoord = (await readline()) || '';
            console.log(emptyCoord);
            continue;
        }

        const newText = [];
        const offsetMap = new Map();
        let isInQuote = false;
        let currentIndex = 0;
        let prevChar;
        for (let i = 0, n = line.length; i < n; i++) {
            if (line[i] === "'") {
                isInQuote = !isInQuote;
                newText.push(line[i]);
                prevChar = line[i];
                offsetMap.set(i, currentIndex++);
                continue;
            }
            if (isInQuote) { // 在单引号包裹范围内
                newText.push(line[i]);
                offsetMap.set(i, currentIndex++);
                prevChar = line[i];
                continue;
            }

            if (line[i] === ' ') {
                if (prevChar !== ' ') { // 保留第一个空格
                    newText.push(line[i]);
                    offsetMap.set(i, currentIndex++);
                    prevChar = line[i];
                } else {
                    offsetMap.set(i, currentIndex - 1);
                    prevChar = line[i];
                }
                continue;
            }

            newText.push(line[i]);
            offsetMap.set(i, currentIndex++);
            prevChar = line[i];
        }

        console.log(newText.join(''));

        if (!coordinates.length) {
            console.log('');
            continue;
        }

        let s = '';
        for (const [oldStart, oldEnd] of coordinates) {
            const newStart = offsetMap.get(oldStart);
            const newEnd = offsetMap.get(oldEnd);
            s += `[${newStart},${newEnd}]`;
        }
        console.log(s);
    }
})();

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值