题目链接
题目描述
给定一个由小写字母组成的字符串,你每次可以替换其中一个字母。求最少需要替换多少次,才能使字符串中不包含 "pacman" 作为子串。
解题思路
本题要求的是消除所有 "pacman" 子串的最小操作次数。这是一个可以运用贪心策略解决的问题。
1. 核心观察
- 操作成本:要破坏一个 "pacman" 子串,我们只需要修改其 6 个字符中的任意一个。因此,消除一个 "pacman" 子串的成本是 1 次替换。
- 子串独立性:目标子串 "pacman" 有一个重要的性质——它没有自重叠。这意味着,字符串 "pacman" 的任何真前缀(如 "p", "pa", ...)都不等于它的任何真后缀(如 "n", "an", ...)。这个性质保证了在一个长字符串中,任意两个 "pacman" 的出现都是完全分离、互不重叠的。
- 结论:由于每个 "pacman" 子串都是独立的,一次替换操作最多只能消除一个 "pacman" 子串。因此,最小替换次数等于字符串中 "pacman" 子串出现的总次数。
2. 贪心算法
基于以上结论,问题就简化为如何高效地计算不重叠的 "pacman" 子串的数量。我们可以使用一个贪心算法来解决:
- 从字符串的第一个字符开始,向后扫描。
- 在当前位置
i
,检查从i
开始的长度为 6 的子串是否等于 "pacman"。 - 如果等于:
- 我们找到了一个 "pacman" 子串。将计数器加一。
- 因为我们已经处理了这 6 个字符,并且知道下一个 "pacman" 不可能在这 6 个字符内开始,所以我们可以安全地将扫描指针向前跳过 6 个位置,从
i + 6
继续搜索。
- 如果不等于:
- 说明当前位置不是 "pacman" 的起点。我们将扫描指针向前移动一位,从
i + 1
继续搜索。
- 说明当前位置不是 "pacman" 的起点。我们将扫描指针向前移动一位,从
重复这个过程直到扫描完整个字符串,最终的计数值就是所需的最少替换次数。
代码
#include <bits/stdc++.h>
using namespace std;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
string s;
cin >> s;
int replacements = 0;
int i = 0;
while (i <= n - 6) {
if (s.substr(i, 6) == "pacman") {
replacements++;
i += 6;
} else {
i++;
}
}
cout << replacements << endl;
return 0;
}
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
String s = sc.next();
int replacements = 0;
int i = 0;
while (i <= n - 6) {
// In Java, substring's end index is exclusive
if (s.substring(i, i + 6).equals("pacman")) {
replacements++;
i += 6;
} else {
i++;
}
}
System.out.println(replacements);
}
}
import sys
def solve():
n = int(sys.stdin.readline())
s = sys.stdin.readline().strip()
replacements = 0
i = 0
while i <= n - 6:
if s[i:i+6] == "pacman":
replacements += 1
i += 6
else:
i += 1
print(replacements)
solve()
算法及复杂度
- 算法:贪心扫描
- 时间复杂度:
,其中
是输入字符串的长度。我们的扫描指针
i
只会从左到右移动一次。 - 空间复杂度:
,用于存储输入的字符串。算法本身只需要常数级别的额外空间。