为了更好的阅读体检,可以查看我的算法学习网
本题在线评测链接:P1361
题目描述
给定一个字符串
的摘要算法,请输出给定字符串的摘要值
- 去除字符串中非字母的符号。
- 如果出现连续字符(不区分大小写),则输出: 该字符(小写) + 连续出现的次数。
- 如果是非连续的字符(不区分大小写),则输出: 该字符(小写)+ 该字母之后字符串中出现的该字符的次数。
- 对按照以上方式表示后的字符串进行排序:字母和紧随的数字作为一组进行排序,数字大的在前,数字相同的,则按字母进行排序,字母小的在前。
输入描述
一行字符串,长度为 [ 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[i−1]进行比较,如果相同,就是连续。由于题目对于连续的字符会进行压缩处理,因此对于连续的字符,我们只需要关注连续了多少次,以及连续的字符是什么即可。因此,我们可以定义一个 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("");
}