阿里巴巴 算法岗笔试真题【坏掉的键盘】

坏掉的键盘(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

题解

思路

逻辑分析

  1. 首先坏掉的键盘肯定是s中未出现字符,初始先统计未出现字符种类个数, count
  2. 首尾两个位置确实各自有两种选择(插或不插),对结果产生影响为2 * 2
  3. 不考虑内部情况暂时得到的结果为res = count * 2 * 2,接下来考虑s字符相关性
    • 相邻字符不同,中间可以插也可以不插(2 种),res * 2
    • 若相邻字符相同,中间必须插入失灵字符(只有 1 种)
  4. 总体算法时间复杂度为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)
	}
}