坏掉的键盘(C++/Py/Java /Js/Go)题解
阿里算法岗 0523笔试 第二题
题目内容
小明准备输入一个仅由小写英文字母组成的字符串,但他的键盘在一开始就有且仅有一个按键失灵,导致该字母在原串中的所有出现都没有被输入,最终得到的字符串为 sss。小明还告诉你:原本要输入的完整字符串中任意相邻两个字符都不相同。
请你计算,对于每一个可能的小写字母 ccc('aaa' 到 'zzz'),有多少个满足条件的原始字符串(删除所有 ccc 后得到 sss,且自身相邻字符不同)。请输出将这些情况下得到的数量相加后的总和。由于答案可能很大,结果对 109+710^9 + 7109+7 取模。
输入描述
每个测试文件包含多组测试数据。第一行输入一个整数 T (1≤T≤105)T\ (1 \le T \le 10^5)T (1≤T≤105) 表示数据组数。接下来每组数据描述如下:
- 第一行输入一个整数 n (1≤n≤2×105)n\ (1 \le n \le 2 \times 10^5)n (1≤n≤2×105) 表示字符串的长度。
- 第二行输入一个仅由小写字母组成的字符串 sss。
保证所有测试数据中字符串长度总和不超过 5×1055 \times 10^55×105。
输出描述
对于每组测试数据,输出一个整数,表示"可能的原始字符串"的数量对 109+710^9 + 7109+7 取模后的结果。
样例1
输入
3
4
abac
4
aaaa
3
xyz
输出
736
100
368
题解
思路
逻辑分析
- 首先坏掉的键盘肯定是s中未出现字符,初始先统计未出现字符种类个数,
count。 - 首尾两个位置确实各自有两种选择(插或不插),对结果产生影响为
2 * 2 - 不考虑内部情况暂时得到的结果为
res = count * 2 * 2,接下来考虑s字符相关性- 相邻字符不同,中间可以插也可以不插(2 种),
res * 2 - 若相邻字符相同,中间必须插入失灵字符(只有 1 种)
- 相邻字符不同,中间可以插也可以不插(2 种),
- 总体算法时间复杂度为
O(n)
C++
cpp
#include<bits/stdc++.h>
using namespace std;
const long MOD = 1e9 + 7;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
string s;
cin >> s;
vector<int> flag(26, false);
// 统计未出现字符数量
int count = 26;
for (int i = 0; i < n; i++) {
char c = s[i];
if (!flag[c - 'a']) {
flag[c - 'a'] = true;
count--;
}
}
long res = count;
// 首尾都可额外添加或不添加 2*2
res *= 4;
// 考虑两个字符
for (int i = 1; i < n; i++) {
// 不等于情况,中间可插入或不插入失灵字符
if (s[i] != s[i-1]) {
res = (res * 2) % MOD;
}
}
cout << res << endl;
}
return 0;
}
java
java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
public class Main {
static final long MOD = 1000000007L;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int T = Integer.parseInt(br.readLine());
while (T-- > 0) {
int n = Integer.parseInt(br.readLine());
String s = br.readLine();
boolean[] flag = new boolean[26];
// 统计未出现字符数量
int count = 26;
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
if (!flag[c - 'a']) {
flag[c - 'a'] = true;
count--;
}
}
long res = count;
// 首尾都可额外添加或不添加 2*2
res *= 4;
// 考虑两个字符
for (int i = 1; i < n; i++) {
// 不等于情况,中间可插入或不插入失灵字符
if (s.charAt(i) != s.charAt(i - 1)) {
res = (res * 2) % MOD;
}
}
System.out.println(res);
}
}
}
python
python
MOD = 10 ** 9 + 7
T = int(input())
for _ in range(T):
n = int(input())
s = input()
flag = [False] * 26
# 统计未出现字符数量
count = 26
for c in s:
idx = ord(c) - ord('a')
if not flag[idx]:
flag[idx] = True
count -= 1
res = count
# 首尾都可额外添加或不添加 2*2
res *= 4
# 考虑两个字符
for i in range(1, n):
# 不等于情况,中间可插入或不插入失灵字符
if s[i] != s[i - 1]:
res = (res * 2) % MOD
print(res)
javascript
js
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on("line", (line) => {
input.push(line);
});
rl.on("close", () => {
const MOD = 1000000007n;
let idx = 0;
const T = Number(input[idx++]);
const ans = [];
for (let t = 0; t < T; t++) {
const n = Number(input[idx++]);
const s = input[idx++];
const flag = new Array(26).fill(false);
// 统计未出现字符数量
let count = 26;
for (let i = 0; i < n; i++) {
const c = s.charCodeAt(i) - 97;
if (!flag[c]) {
flag[c] = true;
count--;
}
}
let res = BigInt(count);
// 首尾都可额外添加或不添加 2*2
res *= 4n;
// 考虑两个字符
for (let i = 1; i < n; i++) {
// 不等于情况,中间可插入或不插入失灵字符
if (s[i] !== s[i - 1]) {
res = (res * 2n) % MOD;
}
}
ans.push(res.toString());
}
console.log(ans.join("\n"));
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
const MOD int64 = 1000000007
func main() {
in := bufio.NewReader(os.Stdin)
var T int
fmt.Fscan(in, &T)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
for ; T > 0; T-- {
var n int
var s string
fmt.Fscan(in, &n)
fmt.Fscan(in, &s)
flag := make([]bool, 26)
// 统计未出现字符数量
count := 26
for i := 0; i < n; i++ {
c := s[i] - 'a'
if !flag[c] {
flag[c] = true
count--
}
}
var res int64 = int64(count)
// 首尾都可额外添加或不添加 2*2
res *= 4
// 考虑两个字符
for i := 1; i < n; i++ {
// 不等于情况,中间可插入或不插入失灵字符
if s[i] != s[i-1] {
res = (res * 2) % MOD
}
}
fmt.Fprintln(out, res)
}
}