【备战秋招】每日一题:2023.05-B卷-华为OD机试 - 字符串摘要

为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1361

题目描述

给定一个字符串的摘要算法,请输出给定字符串的摘要值

  1. 去除字符串中非字母的符号。
  2. 如果出现连续字符(不区分大小写),则输出: 该字符(小写) + 连续出现的次数。
  3. 如果是非连续的字符(不区分大小写),则输出: 该字符(小写)+ 该字母之后字符串中出现的该字符的次数。
  4. 对按照以上方式表示后的字符串进行排序:字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前。

输入描述

一行字符串,长度为 [ 1 , 200 ] [1,200] [1,200]

输出描述

摘要字符串。

样例

输入

aabbcc

输出

a2b2c2

说明

输入

bAaAcBb

输出

a3b2b2c0

说明

b A a A c B b bAaAcBb bAaAcBb:

第一个 b b b非连续字母,该字母之后字符串中还出现了 2 2 2次(最后的两个 B b Bb Bb),所以输出 b 2 b2 b2

a a a连续出现 3 3 3次,输出 a 3 a3 a3

c c c非连续,该字母之后字符串再没有出现过 c c c,输出$c0

Bb$连续 2 2 2次,输出 b 2 b2 b2

b 2 a 3 c 0 b 2 b2a3c0b2 b2a3c0b2进行排序,最终输出 a 3 b 2 b 2 c 0 a3b2b2c0 a3b2b2c0

思路

该题是一道模拟题。

我们发现题目的重点在于如何找到连续与不连续的字符以及如何处理这些字符。我们记字符串为 l e t t e r s letters letters

对于连续字符:

首先我们要知道如何才能了解字符处于连续状态?很明显,我们可以将字符 l e t t e r s [ i ] letters[i] letters[i] l e t t e r s [ i − 1 ] letters[i-1] letters[i1]进行比较,如果相同,就是连续。由于题目对于连续的字符会进行压缩处理,因此对于连续的字符,我们只需要关注连续了多少次,以及连续的字符是什么即可。因此,我们可以定义一个 r e p e a t repeat repeat来记录当前字符连续出现了多少次,用 p r e pre pre来记录连续的字符是什么。之后,我们每次遇到一个相同连续字符, r e p e a t repeat repeat就加1,直到遇到不同字符。一旦遇到不同字符, r e p e a t repeat repeat就要变为1, p r e pre pre就要更新为新字符。

对于非连续字符:

我们发现非连续字符的处理需要求出后续相同字符的个数。因此,我们可以事先统计每一个字符出现的次数 c o u n t [ x ] count[x] count[x],之后从前往后枚举字符的过程中,遇到一个字符 x x x,就使得 c o u n t [ x ] count[x] count[x]减1.这样,每次遇到非连续字符,我们直接将对应的值变为 c o u n t [ x ] count[x] count[x]即可。

类似题目推荐

代码

C++

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <algorithm>

std::string s;

std::string getResult() {
    // 不区分大小写
    std::transform(s.begin(), s.end(), s.begin(), ::tolower);

    // 统计每个字母出现的次数
    std::unordered_map<char, int> count;
    // 去除字符串中的非字母
    std::vector<char> letters;

    for (char c : s) {
        if (c >= 'a' && c <= 'z') {
            count[c]++;
            letters.push_back(c);
        }
    }

    // 加空格是为了避免后续的收尾操作
    s = std::string(letters.begin(), letters.end()) + ' ';
    count[' '] = 1;

    // 记录连续字母和非连续字母
    std::vector<std::pair<char, int>> ans;

    // 上一个位置的字母
    char pre = s[0];
    // 该字母连续次数记为1
    int repeat = 1;
    // 后续该字母还有count[pre]-=1个
    count[pre]--;

    for (size_t i = 1; i < s.length(); i++) {
        // 当前位置的字母
        char cur = s[i];
        // 后续该字母还有count[cur]-=1个
        count[cur]--;

        if (cur == pre) {
            // 如果当前位置和上一个位置的字母相同,则产生连续
            // 连续次数+1
            repeat++;
        } else {
            // 如果当前位置和上一个位置的字母不同,则连续打断
            // 如果pre字母连续次数>1,则是真连续,那么就是pre+repeat,否则就是假连续,是pre+count[pre]
            ans.push_back({pre, (repeat > 1) ? repeat : count[pre]});
            // 更新pre为cur
            pre = cur;
            // 更新pre连续次数为1
            repeat = 1;
        }
    }

    // 字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前
    std::sort(ans.begin(), ans.end(), [](const std::pair<char, int>& a, const std::pair<char, int>& b) {
        return (a.second != b.second) ? (a.second > b.second) : (a.first < b.first);
    });

    std::string result;
    for (const auto& pair : ans) {
        result += pair.first + std::to_string(pair.second);
    }

    return result;
}

int main() {
    std::getline(std::cin, s);
    std::cout << getResult() << std::endl;
    return 0;
}

python

# 输入获取
s = input()
 
 
# 算法入口
def getResult():
    global s
 
    # 不区分大小写
    s = s.lower()
 
    # 统计每个字母出现的次数
    count = {}
    # 去除字符串中的非字母
    letters = []
 
    for c in s:
        if 'z' >= c >= 'a':
            count[c] = count.get(c, 0) + 1
            letters.append(c)
 
    # 加空格是为了避免后续的收尾操作,如果有疑问可以移除下面加空格操作
    s = "".join(letters) + " "
    count[' '] = 1
 
    # 记录连续字母和非连续字母
    ans = []
 
    # 上一个位置的字母
    pre = s[0]
    # 该字母连续次数记为1
    repeat = 1
    # 后续该字母还有count[pre]-=1个
    count[pre] -= 1
 
    for i in range(1, len(s)):
        # 当前位置的字母
        cur = s[i]
        # 后续该字母还有count[cur]-=1个
        count[cur] -= 1
 
        if cur == pre:
            # 如果当前位置和上一个位置的字母相同,则产生连续
            # 连续次数+1
            repeat += 1
        else:
            # 如果当前位置和上一个位置的字母不同,则连续打断
            # 如果pre字母连续次数>1,则是真连续,那么就是pre+repeat,否则就是假连续,是pre+count[pre]
            ans.append([pre, repeat if repeat > 1 else count[pre]])
            # 更新pre为cur
            pre = cur
            # 更新pre连续次数为1
            repeat = 1
 
    # 字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前
    ans.sort(key=lambda x: (-x[1], x[0]))
 
    return "".join(map(lambda x: x[0]+str(x[1]), ans))
 
 
# 算法调用
print(getResult())

Java

import java.util.ArrayList;
import java.util.Scanner;
 
public class Main {
  // 字母数字类
  static class Letter {
    char letter;
    int num;
 
    public Letter(char letter, int num) {
      this.letter = letter;
      this.num = num;
    }
 
    @Override
    public String toString() {
      return this.letter + "" + this.num;
    }
  }
 
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println(getResult(sc.nextLine()));
  }
 
  public static String getResult(String s) {
    // 不区分大小写
    s = s.toLowerCase();
 
    // 统计每个字母出现的次数
    int[] count = new int[128];
 
    // 去除字符串中的非字母
    StringBuilder sb = new StringBuilder();
 
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (c >= 'a' && c <= 'z') {
        count[c]++;
        sb.append(c);
      }
    }
 
    // 加空格是为了避免后续的收尾操作,如果有疑问可以移除下面加空格操作
    s = sb + " ";
 
    // 记录连续字母和非连续字母
    ArrayList<Letter> ans = new ArrayList<>();
 
    // 上一个位置的字母
    char pre = s.charAt(0);
    // 该字母连续次数记为1
    int repeat = 1;
    // 后续该字母还有count[pre]-=1个
    count[pre]--;
 
    for (int i = 1; i < s.length(); i++) {
      // 当前位置的字母
      char cur = s.charAt(i);
      // 后续该字母还有count[cur]-=1个
      count[cur]--;
 
      if (cur == pre) {
        // 如果当前位置和上一个位置的字母相同,则产生连续
        // 连续次数+1
        repeat++;
      } else {
        // 如果当前位置和上一个位置的字母不同,则连续打断
        // 如果pre字母连续次数>1,则是真连续,那么就是pre+repeat,否则就是假连续,是pre+count[pre]
        ans.add(new Letter(pre, repeat > 1 ? repeat : count[pre]));
        // 更新pre为cur
        pre = cur;
        // 更新pre连续次数为1
        repeat = 1;
      }
    }
 
    // 字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前
    ans.sort((a, b) -> a.num != b.num ? b.num - a.num : a.letter - b.letter);
 
    StringBuilder res = new StringBuilder();
    for (Letter an : ans) {
      res.append(an.toString());
    }
    return res.toString();
  }
}

Go

package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strings"
	"unicode"
)

var s string

func getResult() string {
	// 不区分大小写
	s = strings.ToLower(s)

	// 统计每个字母出现的次数
	count := make(map[rune]int)
	// 去除字符串中的非字母
	letters := []rune{}

	for _, c := range s {
		if unicode.IsLetter(c) {
			count[c]++
			letters = append(letters, c)
		}
	}

	// 加空格是为了避免后续的收尾操作
	s = string(letters) + " "
	count[' '] = 1

	// 记录连续字母和非连续字母
	ans := []struct {
		letter rune
		count  int
	}{}

	// 上一个位置的字母
	pre := rune(s[0])
	// 该字母连续次数记为1
	repeat := 1
	// 后续该字母还有count[pre]-=1个
	count[pre]--

	for _, cur := range s[1:] {
		// 后续该字母还有count[cur]-=1个
		count[cur]--

		if cur == pre {
			// 如果当前位置和上一个位置的字母相同,则产生连续
			// 连续次数+1
			repeat++
		} else {
			// 如果当前位置和上一个位置的字母不同,则连续打断
			// 如果pre字母连续次数>1,则是真连续,那么就是pre+repeat,否则就是假连续,是pre+count[pre]
			if repeat > 1 {
				ans = append(ans, struct {
					letter rune
					count  int
				}{letter: pre, count: repeat})
			} else {
				ans = append(ans, struct {
					letter rune
					count  int
				}{letter: pre, count: count[pre]})
			}
			// 更新pre为cur
			pre = cur
			// 更新pre连续次数为1
			repeat = 1
		}
	}

	// 字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前
	sort.Slice(ans, func(i, j int) bool {
		if ans[i].count != ans[j].count {
			return ans[i].count > ans[j].count
		}
		return ans[i].letter < ans[j].letter
	})

	var result strings.Builder
	for _, pair := range ans {
		result.WriteRune(pair.letter)
		result.WriteString(fmt.Sprint(pair.count))
	}

	return result.String()
}

func main() {
	reader := bufio.NewReader(os.Stdin)
	s, _ = reader.ReadString('\n')
	fmt.Println(getResult())
}

Js

/* JavaScript Node ACM模式 控制台输入获取 */
const readline = require("readline");
 
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
 
rl.on("line", (line) => {
  console.log(getResult(line));
});
 
function getResult(s) {
  // 不区分大小写
  s = s.toLowerCase();
 
  // 统计每个字母出现的次数
  const count = {};
 
  // 去除字符串中的非字母
  const letters = [];
 
  for (let c of s) {
    if (c >= "a" && c <= "z") {
      count[c] = (count[c] ?? 0) + 1;
      letters.push(c);
    }
  }
 
  // 加空格是为了避免后续的收尾操作,如果有疑问可以移除下面加空格操作
  s = letters.join("") + " ";
 
  // 记录连续字母和非连续字母
  const ans = [];
 
  // 上一个位置的字母
  let pre = s[0];
  // 该字母连续次数记为1
  let repeat = 1;
  // 后续该字母还有count[pre]-=1个
  count[pre]--;
 
  for (let i = 1; i < s.length; i++) {
    // 当前位置的字母
    const cur = s[i];
    // 后续该字母还有count[cur]-=1个
    count[cur]--;
 
    if (cur == pre) {
      // 如果当前位置和上一个位置的字母相同,则产生连续
      // 连续次数+1
      repeat++;
    } else {
      // 如果当前位置和上一个位置的字母不同,则连续打断
      // 如果pre字母连续次数>1,则是真连续,那么就是pre+repeat,否则就是假连续,是pre+count[pre]
      ans.push([pre, repeat > 1 ? repeat : count[pre]]);
      // 更新pre为cur
      pre = cur;
      // 更新pre连续次数为1
      repeat = 1;
    }
  }
 
  // 字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前
  return ans
    .sort((a, b) =>
      a[1] != b[1] ? b[1] - a[1] : a[0].charCodeAt() - b[0].charCodeAt()
    )
    .map(([letter, num]) => letter + num)
    .join("");
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值