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

- 不考虑关键词起始和结束位置为空格的场景;
- 单词的的开始和结束下标保证涵盖一个完整的单词,即一个坐标对开始和结束下标之间不会有多余的空格;
- 如果有单引号,则用例保证单引号成对出现;
- 关键词可能会重复;
- 文本字符长度 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]
说明
a和picture中间多余的空格进行删除
示例二
输入
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]
说明
a和sum之间有多余的空格,但是因为有成对单引号,不去除多余空格
思路
核心逻辑
- 状态标记:用布尔变量
isInQuote标记是否在单引号内(遇'翻转状态),单引号内保留所有空格,外仅保留连续空格的第一个; - 文本重构+偏移映射:遍历原文本构建新文本,用
offsetMap记录「原下标→新下标」映射(仅添加字符时新下标currentIndex递增,多余空格映射到前一位置); - 坐标更新:基于
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);
}
})();
598

被折叠的 条评论
为什么被折叠?



