关于#字符串#的问题,如何解决?

7-14 字符串约束
分数 100
作者 imtian
单位 临沂大学
给定一个只包含下划线和大写字母的字符串,将下划线全部换成大写字母,问有多少种填法能使这个字符串满足以下三种要求:

不包含三个及以上连续的元音字母;
不包含三个及以上连续的辅音字母;
至少包含一个大写字母 L。
元音字母仅包含A,E,I,O,U,其他的字母都是辅音字母。

输入格式:
输入的一行给出一行字符串S;

∣S∣≤100

题目保证最多含有10个下划线,并且只会出现大写字母和下划线。

输出格式:
输出一个整数——表示方案数。

输入样例:
L_V
输出样例:
5
输入样例:
JA_BU_K_A
输出样例:
485

  • 你可以参考下这个问题的回答, 看看是否对你有帮助, 链接: https://ask.csdn.net/questions/7722867
  • 以下回答由chatgpt基于相关博客总结生成:

    思路简介

    本题实质上是求满足特定条件的字符串数量,因此考虑使用枚举法暴力枚举,在检查每一种情况是否符合要求后计数。

    ① 题目要求把所有下划线换成大写字母,可以使用 C++ 的循环语句遍历字符串,如果遇到下划线则将其替换为对应的大写字母。

    ② 检查元音字母和辅音字母的连续重复情况

    (1)使用 C++ 的 while 循环语句,当有 i,j,k 都存在使得 S[i] == S[j] == S[k] 时结束判断。

    (2) 首先对五个元音字母和辅音字母进行枚举,分别找到所有连续出现的重复字母组合,对每个结果判断中间是否有三次或三次以上的连续出现,有则标记为不符合。值得注意的是,如果字母只能出现一次,则尝试显式写出五个字母的重复情况将涉及大量的枚举和判断,在时间上可能不够充足;如果字母可以出现多次,则可以使用桶排序进行优化,核心思想是建一个大小为 26 的数组表示每个字母的出现次数,方便通过枚举方式直接找到二次重复以上的部分。

    ③ 检查是否至少包含一个大写字母 L

    输入的字符串中不含小写字母,因此只需要在循环遍历时判断元素是否为大写字母 L 即可。

    复杂度分析

    时间复杂度:本题一共由三个部分构成,依次枚举下划线的情况和检查连续重复的情况每部分的复杂度均为 $\mathcal{O}(1)$,最后 C++ 的循环遍历复杂度为 $\mathcal{O}(n)$,因此总时间复杂度为 $\mathcal{O}(n)$。

    空间复杂度:小于 $\mathcal{O}(n)$,为了判断连续重复按字典序排列的 5 个字母是否合法及桶排序的需要,考虑建立大小均为 26 的滑动窗口和数组,且是较短的常量级别操作。

    参考代码

    #include <bits/stdc++.h>
    using namespace std;
    
    // 简单的字符映射
    inline char tonum(string &s, int pos) {
        if (s[pos] == '_') return 'A' + pos;
        return s[pos];
    }
    
    // 挑选变动元素进行元音字母、辅音字母的枚举
    int vowels[11] = {0, 1, 4, 8, 14, 20};
    int consonants[17] = {2, 3, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 17, 18, 19, 21, 22};
    
    bool for_check_correctness(string &s, int y1, int y2, int y3) {
        int n = s.size();
        // 替换元音字母和辅音字母为字母 A,以便进行简单的枚举
        for (int i = 0; i < n; i++) {
            char ch = s[i];
            if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U')
                s[i] = 'A';
            else
                s[i] = 'B';
        }
        s[y1] = 'A'; s[y2] = 'A'; s[y3] = 'A';  // 用元音字母替换下划线
        bool is_consecutive = false;
        for (int i = 0; !is_consecutive && i < 10; i++) {
            int consec = 1;
            char pre = s[vowels[i]];
            for (int j = 1; j < 3; j++) {
                char cur = s[vowels[i + j]];
                if (cur == pre)
                    consec++;
                else
                    consec = 1;
                if (consec >= 3) {
                    is_consecutive = true;
                    break;
                }
                pre = cur;
            }
        }
        if (is_consecutive) return false;
        is_consecutive = false;
        for (int i = 0; !is_consecutive && i < 17; i++) {
            int consec = 1;
            char pre = s[consonants[i]];
            for (int j = 1; j < 3; j++) {
                char cur = s[consonants[i + j]];
                if (cur == pre)
                    consec++;
                else
                    consec = 1;
                if (consec >= 3) {
                    is_consecutive = true;
                    break;
                }
                pre = cur;
            }
        }
        if (is_consecutive) return false;
        for (int i = 0; i < n; i++) // 恢复字符串,由 A 和 B 映射回原来的字母
            if (s[i] == 'A' || s[i] == 'B')
                s[i] = tonum(s, i);
        return s.find('L') != string::npos;  // 判断是否包含字母 L
    }
    
    int main() {
        string S;
        cin >> S;
        int cnt = 0;
        int n = S.size(), idx[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        do {
            string cur = S;
            for (int p = 0; p < 1024; p++) {  // 暴力枚举下划线的情况
                int num = p;
                for (int j = 0; j < n; j++)
                    if (cur[j] == '_') {
                        cur[j] = 'A' + num % 26;  // 对下划线进行某个字母的复原
                        num /= 26;
                    }
                bool is_correct = false;
                for (int k1 = 0; k1 < n; k1++)
                    for (int k2 = k1 + 1; k2 < n; k2++)
                        for (int k3 = k2 + 1; k3 < n; k3++)
                            if (cur[k1] == '_' && cur[k2] == '_' && cur[k3] == '_') {
                                is_correct = for_check_correctness(cur, k1, k2, k3);
                                if (is_correct) goto OUT;  // 使用 goto 跳过当前循环
                            }
            }
            OUT:
            if (is_correct) ++cnt;
        } while (next_permutation(idx + 1, idx + n - 1));  // 暴力枚举下划线的排列
        cout << cnt << endl;
        return 0;
    }