P11451 [USACO24DEC] It‘s Mooin‘ Time B(枚举算法)

 题目描述

Farmer John 正在试图向 Elsie 描述他最喜欢的 USACO 竞赛,但她很难理解为什么他这么喜欢它。他说「竞赛中我最喜欢的部分是 Bessie 说 『现在是哞哞时间』并在整个竞赛中一直哞哞叫」。

Elsie 仍然不理解,所以 Farmer John 将竞赛以文本文件形式下载,并试图解释他的意思。竞赛被定义为一个长度为 $N$($3≤N≤20000$)的小写字母字符串。一种哞叫一般地定义为子串 $c_ic_jc_j$,其中某字符 $c_i$ 之后紧跟着 $2$ 个某字符 $c_j$,且 $c_i≠c_j$。根据 Farmer John 的说法,Bessie 哞叫了很多,所以如果某种哞叫在竞赛中出现了至少 $F$($1≤F≤N$)次,那可能就是 Bessie 发出的。

然而,Farmer John 的下载可能损坏,文本文件可能存在至多一个字符与原始文件不同。将可能的误差考虑在内,输出所有可能是 Bessie 发出的哞叫,按字典序顺序排序。

输入格式

输入的第一行包含 $N$ 和 $F$,表示字符串的长度以及 Bessie 的哞叫的频次下限。

第二行包含一个长度为 $N$ 的小写字母字符串,表示竞赛。

输出格式

输出可能是 Bessie 发出的哞叫的数量,以下是按字典序排序的哞叫列表。每行输出一种哞叫。

 输入输出样例 #1

输入 #1


10 2
zzmoozzmoo
 

输出 #1


1
moo
 

 输入输出样例 #2

输入 #2


17 2
momoobaaaaaqqqcqq
```

输出 #2


3
aqq
baa
cqq
```

 输入输出样例 #3

输入 #3


3 1
ooo
```

 输出 #3


25
aoo
boo
coo
doo
eoo
foo
goo
hoo
ioo
joo
koo
loo
moo
noo
poo
qoo
roo
soo
too
uoo
voo
woo
xoo
yoo
zoo
```

 说明/提示

样例 #1 解释

在这个样例中,任何字符变化都不会影响答案。唯一 Bessie 可能发出的哞叫是 $\tt{moo}$。

样例 #2 解释

在这个样例中,位置 $8$(从零开始索引)的 $\tt{a}$ 可能是由 $\tt b$ 损坏导致的,这使得 $\tt baa$ 成为一种 Bessie 发出两次的可能的哞叫。此外,位置 $11$ 的 $\tt q$ 可能是由 $\tt c$ 损坏导致的,这使得 $\tt cqq$ 成为一种 Bessie 可能的哞叫。$\tt aqq$ 可以通过将 $\tt c$ 换成 $\tt a$ 来达到。

 测试点性质
- 测试点 1-3:样例。
- 测试点 4-8:$N≤100$。
- 测试点 9-13:没有额外限制。

代码:

#include 
#include 
#include 
#include 
using namespace std;

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n, f;
    string s;
    vector Vec;
    cin >> n >> f;
    cin >> s;

    for (char c1 = 'a'; c1 <= 'z'; c1++)
    {
        for (char c2 = 'a'; c2 <= 'z'; c2++)
        {
            if (c1 == c2)
                continue;

            string temp = s;
            int ans = 0;
            // 统计原始符合条件的子串数目,并标记为###
            for (int i = 0; i + 2 < n; i++)
            {
                if (temp[i] == c1 && temp[i + 1] == c2 && temp[i + 2] == c2)
                {
                    ans++;
                    temp[i] = temp[i + 1] = temp[i + 2] = '#';
                }
            }

            int cnt = 0;
            // 检查是否存在未被标记的位置可以通过修改一个字符形成符合条件的子串
            for (int i = 0; i + 2 < n; i++)
            {
                if (temp[i] == '#' || temp[i + 1] == '#' || temp[i + 2] == '#')
                    continue;

                // 检查三种可能的修改情况
                if ((temp[i] == c1 && temp[i + 1] == c2) ||
                    (temp[i] == c1 && temp[i + 2] == c2) ||
                    (temp[i + 1] == c2 && temp[i + 2] == c2))
                {
                    cnt = 1;
                    break; // 找到一个即可,退出循环
                }
            }

            if (ans + cnt >= f)
            {
                string t;
                t += c1;
                t += c2;
                t += c2;
                Vec.push_back(t);
            }
        }
    }

    // 按字典序排序
    sort(Vec.begin(), Vec.end());

    cout << Vec.size() << endl;
    for (const auto &str : Vec)
    {
        cout << str << endl;
    }
    return 0;
}

代码解释

  1. 遍历字符组合:使用双重循环遍历所有可能的字符对 (c1, c2),并跳过 c1 和 c2 相同的情况。

  2. 统计原始哞叫数量:对每个字符对,遍历字符串统计符合条件的不重叠子串,并将这些子串标记为 # 以避免重复统计。

  3. 检查修改后的情况:对于每个未被标记的三字符组,检查是否可以通过修改一个字符使其符合哞叫形式。只要存在一个可能的修改位置,则视为可以增加一次哞叫。

  4. 结果处理:将所有可能的哞叫形式按字典序排序后输出。

该方法确保在处理每个字符对时使用原字符串的副本,避免相互干扰,并且正确考虑所有可能的字符组合,包括字符 'z'。

运算过程:

输入

17 2
momoobaaaaaqqqcqq

字符串分解

索引: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
字符: m o m o o b a a a a a  q  q  q  c  q  q

分析过程

1. 处理字符对 (b, a) → 哞叫形式 "baa"
  • 原始匹配:索引5-7为 b a a,计数 ans = 1,标记为 ###

  • 修改检查:检查其他位置是否存在可通过修改一次形成 b a a 的结构。

    • 索引8-10的 a a a:将索引8的 a 改为 b,形成 b a a,增加一次计数 cnt = 1

  • 总次数ans + cnt = 2,满足条件,加入结果列表。

2. 处理字符对 (c, q) → 哞叫形式 "cqq"
  • 原始匹配:索引14-16为 c q q,计数 ans = 1,标记为 ###

  • 修改检查:检查其他位置是否存在可通过修改一次形成 c q q 的结构。

    • 索引11-13的 q q q:将索引11的 q 改为 c,形成 c q q,增加一次计数 cnt = 1

  • 总次数ans + cnt = 2,满足条件,加入结果列表。

3. 处理字符对 (a, q) → 哞叫形式 "aqq"
  • 原始匹配:索引10-12为 a q q,计数 ans = 1,标记为 ###

  • 修改检查:检查其他位置是否存在可通过修改一次形成 a q q 的结构。

    • 索引14-16的 c q q:将索引14的 c 改为 a,形成 a q q,增加一次计数 cnt = 1

  • 总次数ans + cnt = 2,满足条件,加入结果列表。

输出结果

将符合条件的哞叫形式按字典序排序后输出:

3
aqq
baa
cqq

你可能感兴趣的:(算法竞赛题目超详细解析,算法,c语言,c++,青少年编程,贪心算法,推荐算法)