返回

OI 初学者的利器:OpenJudge 聪明的打字员

后端

聪明的打字员:一个提升你 OI 算法技能的绝佳挑战

子标题 1:踏上 OI 征程,迈过实战训练这道坎

信息学奥林匹克竞赛(OI)是一项激动人心的智力竞赛,它考察选手解决复杂算法问题的创造力和逻辑思维能力。对于初学者来说,在入门阶段往往会遇到一个障碍:缺乏实战训练。

OpenJudge 是一个弥补这一不足的绝佳平台,它提供了大量精选的竞赛题目,其中 [OpenJudge 186/洛谷 P1949/NOI 2001] 聪明的打字员 便是其中之一。本文将深入剖析这道题目,帮助你轻松入门 OI,迈过实战训练这道坎。

子标题 2:聪明的打字员:回文子串处理的经典案例

聪明的打字员是一个经典的字符串处理问题。给你定一个长度为 n 的字符串 S,其中包含大小写字母和数字,请计算有多少个不同的子串(不包括空串)是回文的。

回文串是指正读和反读都相同的字符串。例如,字符串 "abba" 和 "12321" 都是回文串。

子标题 3:算法分析:暴力解法与 Manacher 算法

对于聪明的打字员这道题目,我们可以采用两种解法:

  1. 暴力解法: 最简单的解法是暴力枚举所有可能的子串,并判断每个子串是否回文。对于长度为 n 的字符串,共有 n*(n+1)/2 个子串,因此暴力解法的复杂度为 O(n^3)。

  2. Manacher 算法: Manacher 算法是一种线性的算法,可以解决回文子串问题。它的基本思想是预处理出一个长度为 2n+1 的新字符串 T,其中在每个原字符串字符之间插入一个分隔符(例如 #),并从左到右扫描 T,维护一个以当前位置为中心的回文串的半径。

通过巧妙地利用分隔符,我们可以避免重复计算子串的回文性,从而达到线性的时间复杂度。具体实现细节可以参考算法导论或其他相关资料。

子标题 4:代码实现:C++、Python、Java

为了帮助你更好地理解算法,我们提供了三种不同语言的代码示例:

// C++ 代码示例
#include <iostream>
#include <string>
using namespace std;

const int MAXN = 1000005;
int p[MAXN], n;
string s, t;

void Manacher() {
    int r = 0, c = 0;
    for (int i = 1; i <= n; i++) {
        int i_mirror = 2 * c - i;
        if (i < r) {
            p[i] = min(r - i, p[i_mirror]);
        }
        while (i - p[i] - 1 >= 0 && i + p[i] + 1 <= n && t[i - p[i] - 1] == t[i + p[i] + 1]) {
            p[i]++;
        }
        if (i + p[i] > r) {
            r = i + p[i];
            c = i;
        }
    }
}

int main() {
    cin >> n;
    cin >> s;
    t = "#" + s + "#";
    n = t.size();
    Manacher();
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += p[i] / 2;
    }
    cout << ans << endl;
    return 0;
}
# Python 代码示例
def Manacher(s):
    t = "#" + "#".join(s) + "#"
    n = len(t)
    p = [0] * n
    r = 0
    c = 0
    ans = 0
    for i in range(1, n):
        i_mirror = 2 * c - i
        if i < r:
            p[i] = min(r - i, p[i_mirror])
        while i - p[i] - 1 >= 0 and i + p[i] + 1 < n and t[i - p[i] - 1] == t[i + p[i] + 1]:
            p[i] += 1
        if i + p[i] > r:
            r = i + p[i]
            c = i
        ans += p[i] // 2
    return ans

s = input()
print(Manacher(s))
# Java 代码示例
import java.util.Scanner;

public class Main {
    private static final int MAXN = 1000005;
    private static int[] p;
    private static int n;
    private static String s, t;

    private static void Manacher() {
        int r = 0, c = 0;
        for (int i = 1; i <= n; i++) {
            int i_mirror = 2 * c - i;
            if (i < r) {
                p[i] = Math.min(r - i, p[i_mirror]);
            }
            while (i - p[i] - 1 >= 0 && i + p[i] + 1 <= n && t.charAt(i - p[i] - 1) == t.charAt(i + p[i] + 1)) {
                p[i]++;
            }
            if (i + p[i] > r) {
                r = i + p[i];
                c = i;
            }
        }
    }

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        n = input.nextInt();
        s = input.next();
        t = "#" + s + "#";
        n = t.length();
        p = new int[MAXN];
        Manacher();
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans += p[i] / 2;
        }
        System.out.println(ans);
        input.close();
    }
}

子标题 5:结语

通过对 OpenJudge 聪明的打字员题目的剖析,我们不仅掌握了一道经典的算法题,更重要的是了解了回文子串问题的解决思路和算法技巧。对于 OI 初学者来说,OpenJudge 是一个宝贵的学习平台,通过不断地练习和思考,你一定能够提升自己的算法能力,为 OI 竞赛打下坚实的基础。

常见问题解答:

  1. Q:什么是回文串?
    A:回文串是指正读和反读都相同的字符串。例如,字符串 "abba" 和 "12321" 都是回文串。

  2. Q:Manacher 算法的时间复杂度是多少?
    A:Manacher 算法的时间复杂度为 O(n),其中 n 是字符串的长度。

  3. Q:为什么 OpenJudge 是学习 OI 算法的一个好平台?
    A:OpenJudge 提供了大量精选的竞赛题目,涵盖了各种算法知识点。通过练习 OpenJudge 的题目,初学者可以逐步提升自己的算法能力,为 OI 竞赛做好准备。

  4. Q:如何提高解决算法问题的效率?
    A:解决算法问题的效率可以通过多练习、多思考和多总结来提高。平时可以多做一些算法练习题,多思考算法背后的原理和技巧,并总结出一些常用的算法模板和解题思路。

  5. Q:OI 竞赛中有哪些常见的算法类型?
    A:OI 竞赛中常见的算法类型包括贪心算法、动态规划、搜索算法、图论算法、数论算法等。