算法入门篇(八) 之 查找算法

目录

一、哈希表

哈希函数

哈希函数的应用

常见的哈希函数

线性探测、二次探测、链地址

1. 线性探测(Linear Probing)

2. 二次探测(Quadratic Probing)

3. 链地址法(Chaining)

4. 总结

POJ3349、POJ1840、POJ2002

POJ 3349 - Ancient Cipher

POJ 1840 - Maximum Number

POJ 2002 - Tournament Scheduling

二、字符串模式匹配

BF算法、KMP算法

BF算法(Brute Force)

算法介绍

算法步骤

时间复杂度

代码示例(Python)

2. KMP算法(Knuth-Morris-Pratt)

算法介绍

核心思想

算法步骤

时间复杂度

代码示例(Python)

部分匹配表示例

3. 总结

P1308、P3375、HDU2087

1. P1308 - 统计单词数

题目描述

解题思路

代码示例(Python)

2. P3375 - 字符串匹配KMP

题目描述

解题思路

代码示例(Python)

3. HDU2087 - String Matching

题目描述

解题思路

代码示例(Python)

4. 总结

三、二叉查找树

POJ1577、POJ2309,POJ2418、HDU3791

1. POJ1577 - Periodic Strings

题目描述

解题思路

代码示例(Python)

2. POJ2309 - Optimal Milking

题目描述

解题思路

代码示例(Python)

3. POJ2418 - Frequent Words

题目描述

解题思路

代码示例(Python)

4. HDU3791 - Treasure Hunter

题目描述

解题思路

代码示例(Python)

5. 总结

四、平衡二叉树

TOJ3374、POJ3481、POJ1442,POJ2418

1. TOJ3374 - 最小表示法

题目描述

解题思路

代码示例(Python)

2. POJ3481 - Crossing River

题目描述

解题思路

代码示例(Python)

3. POJ1442 - Power Network

题目描述

解题思路

代码示例(Python,使用 Edmonds-Karp 算法)

4. POJ2418 - Frequent Words

题目描述

解题思路

代码示例(Python)

5. 总结


一、哈希表

  • 1.哈希函数

哈希函数(Hash Function)是一种将任意大小的数据(通常是字符串或文件)映射为固定大小的值的算法。这个固定大小的值通常称为哈希值、散列值、或消息摘要。哈希函数的主要特点如下:

固定输出长度:无论输入数据的大小或长度如何,哈希函数的输出总是一个固定长度的哈希值。例如,SHA-256 哈希函数总是生成 256 位(32 字节)的输出。

快速计算:给定一个输入,哈希函数应该能够快速地计算出哈希值。

不可逆性:哈希函数应该是单向的,这意味着从输出哈希值很难反推出原始输入数据。

抗碰撞性:理想情况下,两个不同的输入应该不会产生相同的哈希值。这种特性称为抗碰撞性(collision resistance)。

输入敏感性:对输入的任何微小变化都会导致哈希值的大幅变化。这种特性称为雪崩效应(Avalanche Effect)。

哈希函数的应用

  1. 数据完整性验证:在传输或存储数据时,哈希值可以用于验证数据是否被篡改。比如,下载文件时,网站会提供文件的哈希值,用户可以对下载后的文件进行哈希计算并与提供的哈希值进行比对,以确保文件没有损坏或被修改。

  2. 数字签名与加密:在密码学中,哈希函数常用于生成数字签名或用于消息认证码(MAC),以确保信息的真实性和完整性。

  3. 散列表:在计算机科学中,哈希函数用于散列表(Hash Table),这是一种用于快速查找和存储数据的数据结构。

  4. 密码存储:在系统中,用户密码通常不会直接存储,而是存储其哈希值。这样即使数据库泄露,攻击者也无法轻易得到原始密码。

常见的哈希函数

  • MD5(Message Digest Algorithm 5):生成128位(16字节)的哈希值,但由于碰撞问题,不再推荐使用。
  • SHA-1(Secure Hash Algorithm 1):生成160位(20字节)的哈希值,但也存在安全性问题。
  • SHA-256:SHA-2系列中的一种,生成256位的哈希值,安全性较高,广泛应用于加密货币、数据验证等领域。

哈希函数是现代计算机科学和密码学中的核心工具,其广泛应用于各种数据处理和安全保障场景中。

  • 2.线性探测、二次探测、链地址

线性探测、二次探测和链地址是解决散列表(Hash Table)中哈希冲突(即两个不同的键被映射到相同的哈希值)问题的常用方法。下面对这些方法进行详细介绍。

1. 线性探测(Linear Probing)

线性探测是一种开放寻址法(Open Addressing)的冲突解决策略。当发生哈希冲突时,线性探测通过检查散列表中下一个位置来寻找空槽(slot)进行插入。具体步骤如下:

  • 插入:当插入一个元素时,如果哈希位置已经被占用(冲突发生),则检查当前位置之后的一个位置(即索引增加1)是否为空。如果仍被占用,则继续往下一个位置,直到找到空槽为止。
  • 查找:查找时从哈希位置开始,如果当前槽不是目标元素,则继续检查下一个槽,直到找到元素或遇到空槽为止。

优点

  • 实现简单,容易理解。

缺点

  • 可能会出现“聚集现象”(clustering),即大量冲突的元素集中在某个区域,导致查找效率降低。

2. 二次探测(Quadratic Probing)

二次探测也是一种开放寻址法,但与线性探测不同的是,它通过二次函数的步长来查找空槽,从而减少线性探测中的聚集现象。具体步骤如下:

  • 插入:当发生哈希冲突时,首先检查 hash(key)+12hash(key) + 1^2hash(key)+12 的位置,如果该位置也被占用,则检查 hash(key)+22hash(key) + 2^2hash(key)+22 的位置,依此类推,直到找到空槽。

  • 查找:类似于插入,查找时从哈希位置开始,以二次步长方式探测,直到找到元素或遇到空槽为止。

优点

  • 减少了一定程度的聚集现象,相比线性探测更加高效。

缺点

  • 由于探测位置是非线性的,有时可能会出现探测不到表中的空槽,即存在“二次聚集”的问题。

3. 链地址法(Chaining)

链地址法(又称为拉链法)是另一种常见的冲突解决方法。不同于开放寻址法,链地址法为每个散列表槽存储一个链表(或其他动态数据结构),以容纳所有哈希到该位置的元素。具体步骤如下:

  • 插入:当发生哈希冲突时,直接将新元素插入到相应槽的链表中。

  • 查找:查找时,从哈希位置开始,遍历链表中的所有元素,直到找到目标元素或链表结束为止。

优点

  • 更加灵活,容易处理大量冲突的情况。
  • 不会像开放寻址法那样出现表满无法插入的问题,只要链表能增长,就能处理任意数量的冲突。

缺点

  • 在最坏情况下(所有元素都哈希到同一位置),查找效率可能降低到线性时间复杂度。
  • 需要额外的空间来存储链表指针。

4. 总结

  • 线性探测:简单,但容易出现聚集现象,适合表内空槽较多的情况。
  • 二次探测:减少了聚集现象,但探测位置更复杂,可能出现探测不到空槽的情况。
  • 链地址法:使用链表存储冲突元素,适合大量冲突的场景,但需要额外空间。

每种方法都有其优缺点,具体使用哪种方法需要根据具体应用场景中的冲突率、查找效率要求和空间复杂度来权衡。

  • 3.POJ3349、POJ1840、POJ2002

POJ 3349 - Ancient Cipher

题目描述: 给定两个字符串 s1 和 s2,判断能否通过对字符串中的字符重新排列,使得 s1 和 s2 相等。换句话说,判断这两个字符串是否是字母异位词(anagram)。

解题思路

  • 统计两个字符串中每个字符的出现次数,然后比较两个字符串中每个字符的频率是否相同。
  • 可以使用计数排序来统计字符频率,或者使用 Python 的 collections.Counter 类。

具体步骤

  1. 创建两个长度为 26 的数组来记录两个字符串中每个字母的出现次数。
  2. 遍历字符串,将相应的字符计数增加。
  3. 比较两个计数数组是否相等。

复杂度: 时间复杂度为 O(n),其中n 为字符串的长度。

代码示例:

def is_anagram(s1, s2):
    if len(s1) != len(s2):
        return False
    
    # 使用Counter统计每个字符出现的频率
    from collections import Counter
    return Counter(s1) == Counter(s2)

# 输入两个字符串
s1 = input().strip()
s2 = input().strip()

if is_anagram(s1, s2):
    print("YES")
else:
    print("NO")

POJ 1840 - Maximum Number

题目描述: 给定一个长度为 n 的序列 a1​,a2​,…,an​,要求找到两个数 x 和 y,使得 ax+ay的值最大,并且 x 和 y 的值满足 x+y≤k。

解题思路

  • 遍历序列并计算所有可能的 ax+ay,但要确保满足 x+y≤k 的条件。
  • 可以使用双重循环来遍历所有可能的 x 和 y,记录满足条件的最大值。

具体步骤

  1. 初始化一个最大值变量为负无穷。
  2. 使用双重循环遍历所有可能的 x 和 y,并计算 ax+ay。
  3. 如果 x+y≤k,更新最大值。
  4. 最后输出最大值。

复杂度: 时间复杂度为 O(n2)。

代码示例:

def max_sum_with_condition(arr, k):
    n = len(arr)
    max_sum = float('-inf')  # 初始化最大和为负无穷

    for x in range(n):
        for y in range(x, n):
            if x + y + 2 <= k:  # 这里加2是因为题目索引从1开始,而Python从0开始
                max_sum = max(max_sum, arr[x] + arr[y])
    
    return max_sum

# 输入数据
n, k = map(int, input().split())
arr = list(map(int, input().split()))

# 输出结果
print(max_sum_with_condition(arr, k))

POJ 2002 - Tournament Scheduling

题目描述: 给定 nnn 名选手,安排一个循环赛日程,使得每个选手都与其他所有选手比赛一次,且在每一天的比赛中,每个选手只参加一场比赛。需要输出比赛的日程安排。

解题思路

  • 这是一个经典的“循环赛日程问题”,可以使用递归分治的方式生成日程表。对于偶数 nnn,可以将选手分为两部分,然后将这两部分中的选手交叉配对。对于奇数 nnn,可以增加一个虚拟选手,使得选手人数变为偶数,然后在每轮中,将虚拟选手跳过即可。

具体步骤

  1. 如果 nnn 是奇数,添加一个虚拟选手。
  2. 使用递归的方法生成日程表:首先对一半的选手安排比赛,然后将这部分比赛进行翻转,以安排与另一半选手的比赛。
  3. 输出生成的日程表。

复杂度: 时间复杂度为 O(nlog⁡n)。

代码示例:

def schedule_tournament(n):
    # 如果是奇数,添加一个虚拟选手
    if n % 2 == 1:
        n += 1
    
    # 初始化比赛日程表
    schedule = [[0] * n for _ in range(n - 1)]

    # 使用循环构造赛程
    for i in range(n - 1):
        for j in range(n // 2):
            # 设置比赛对阵表
            schedule[i][j] = (i + j) % (n - 1)
            schedule[i][n - 1 - j] = (i - j + n - 1) % (n - 1)
        
        # 最后一行与其他所有行的比赛不同
        schedule[i][0] = i

    # 输出比赛日程表
    for i in range(n - 1):
        for j in range(n // 2):
            if schedule[i][j] != n - 1:
                print(schedule[i][j] + 1, schedule[i][n - 1 - j] + 1)

# 输入选手数量
n = int(input().strip())
schedule_tournament(n)

二、字符串模式匹配

  • BF算法、KMP算法

BF算法(Brute Force,暴力匹配算法)和KMP算法(Knuth-Morris-Pratt算法)是两种常用的字符串匹配算法。它们用于在一个文本字符串中查找一个模式字符串出现的位置。下面详细介绍这两种算法。

1. BF算法(Brute Force)

算法介绍

BF算法(暴力匹配算法)是一种最简单的字符串匹配方法。它逐个字符地比较文本字符串和模式字符串,直到找到匹配或遍历完所有可能的匹配位置。

算法步骤
  1. 假设文本字符串为 text,模式字符串为 pattern,文本长度为 n,模式长度为 m
  2. text 的第一个字符开始,依次与 pattern 的字符进行比较。如果匹配,继续比较下一个字符;如果不匹配,移动 patterntext 的下一个位置,再次进行比较。
  3. 重复上述过程,直到找到匹配或文本字符串被遍历完毕。
时间复杂度
  • 最坏情况:时间复杂度为 O(n×m)O(n \times m)O(n×m),即对于每个位置,都要进行一次完整的模式匹配尝试。
代码示例(Python)
def brute_force_search(text, pattern):
    n = len(text)
    m = len(pattern)
    
    for i in range(n - m + 1):
        j = 0
        while j < m and text[i + j] == pattern[j]:
            j += 1
        if j == m:
            return i  # 找到匹配,返回起始索引
    
    return -1  # 未找到匹配

# 示例
text = "abcabcabcd"
pattern = "abcd"
result = brute_force_search(text, pattern)
print(result)  # 输出 6

2. KMP算法(Knuth-Morris-Pratt)

算法介绍

KMP算法是一种高效的字符串匹配算法。它通过预处理模式字符串,生成部分匹配表(Partial Match Table,也叫前缀函数表),利用这些信息避免重复比较,从而提高匹配效率。

核心思想
  • 部分匹配表:部分匹配表记录了模式字符串的前缀和后缀的匹配信息。通过这个表,可以在匹配失败时,快速跳过一些不必要的比较,而不需要回溯文本字符串的位置。
算法步骤
  1. 构建部分匹配表:创建一个数组 next,其中 next[i] 表示在模式字符串中,当模式的第 i 个字符匹配失败时,模式应该跳转到 next[i] 位置继续匹配。这个表通过预处理模式字符串生成。
  2. 匹配过程:在文本字符串中与模式字符串进行匹配时,如果字符匹配失败,模式字符串跳转到部分匹配表中指定的位置,而不需要回退文本字符串的索引。
时间复杂度
  • 时间复杂度:KMP算法的时间复杂度为 O(n+m)O(n + m)O(n+m),其中 nnn 是文本字符串长度,mmm 是模式字符串长度。构建部分匹配表需要 O(m)O(m)O(m) 时间,而匹配过程需要 O(n)O(n)O(n) 时间。
代码示例(Python)
def compute_prefix_function(pattern):
    m = len(pattern)
    next = [0] * m
    j = 0  # 当前匹配前缀的长度
    
    for i in range(1, m):
        # 回溯到合适的位置
        while j > 0 and pattern[i] != pattern[j]:
            j = next[j - 1]
        
        if pattern[i] == pattern[j]:
            j += 1
        next[i] = j
    
    return next

def kmp_search(text, pattern):
    n = len(text)
    m = len(pattern)
    next = compute_prefix_function(pattern)
    j = 0  # 模式字符串的指针
    
    for i in range(n):
        # 如果字符不匹配,调整模式字符串的指针
        while j > 0 and text[i] != pattern[j]:
            j = next[j - 1]
        
        if text[i] == pattern[j]:
            j += 1
        
        if j == m:
            return i - m + 1  # 找到匹配,返回起始索引
    
    return -1  # 未找到匹配

# 示例
text = "abcabcabcd"
pattern = "abcd"
result = kmp_search(text, pattern)
print(result)  # 输出 6
部分匹配表示例

假设模式字符串为 "ababaca",其部分匹配表如下:

  • 模式字符串:a b a b a c a
  • 部分匹配表:0 0 1 2 3 0 1

3. 总结

  • BF算法:通过逐个字符比较实现字符串匹配,简单直观,但效率较低,尤其在匹配失败后要回溯文本字符串。
  • KMP算法:通过预处理模式字符串,构建部分匹配表,提高匹配效率,避免了重复比较,适用于较长文本和模式的匹配场景。

KMP算法在实践中比BF算法更加高效,特别是在需要大量匹配操作的情况下。

  • P1308、P3375、HDU2087

1. P1308 - 统计单词数

题目描述

给定一个字符串和一个单词,要求统计该单词在字符串中出现的次数,并输出第一次出现的位置。

解题思路
  1. 将字符串和单词都转换为小写,忽略大小写差异。
  2. 将字符串分割为单词列表,判断每个单词是否等于给定的单词。
  3. 记录匹配次数,并输出第一次出现的位置。
代码示例(Python)
def count_word_occurrences(text, word):
    # 将文本和目标单词转换为小写
    text = text.lower()
    word = word.lower()
    
    # 将文本分割为单词列表
    words = text.split()
    
    # 统计出现次数和第一次出现的位置
    count = 0
    first_pos = -1
    for i, w in enumerate(words):
        if w == word:
            count += 1
            if first_pos == -1:
                first_pos = i
    
    if count > 0:
        # 输出单词出现次数和第一次出现的位置(注意位置从1开始)
        print(f"{count} {text.find(word)}")
    else:
        print(-1)

# 示例输入
text = input().strip()
word = input().strip()
count_word_occurrences(text, word)

2. P3375 - 字符串匹配KMP

题目描述

给定一个模式字符串 pattern 和一个文本字符串 text,要求找到 patterntext 中的所有匹配位置。

解题思路
  1. KMP算法:通过构建部分匹配表(next数组),避免回溯,提升匹配效率。
  2. 使用KMP算法在 text 中查找 pattern,并记录匹配位置。
代码示例(Python)
def compute_prefix_function(pattern):
    m = len(pattern)
    next = [0] * m
    j = 0
    
    for i in range(1, m):
        while j > 0 and pattern[i] != pattern[j]:
            j = next[j - 1]
        if pattern[i] == pattern[j]:
            j += 1
        next[i] = j
    
    return next

def kmp_search(text, pattern):
    n = len(text)
    m = len(pattern)
    next = compute_prefix_function(pattern)
    j = 0
    
    positions = []
    
    for i in range(n):
        while j > 0 and text[i] != pattern[j]:
            j = next[j - 1]
        if text[i] == pattern[j]:
            j += 1
        if j == m:
            positions.append(i - m + 1 + 1)  # +1 是因为题目要求位置从1开始
            j = next[j - 1]
    
    return positions

# 示例输入
text = input().strip()
pattern = input().strip()
positions = kmp_search(text, pattern)

for pos in positions:
    print(pos)

next_array = compute_prefix_function(pattern)
print(" ".join(map(str, next_array)))

3. HDU2087 - String Matching

题目描述

给定一个文本字符串 text 和一个模式字符串 pattern,统计 patterntext 中不重叠出现的次数。

解题思路
  1. KMP算法:为了高效匹配,可以使用KMP算法找到所有模式字符串的匹配位置。
  2. 匹配到一个模式字符串后,跳过模式字符串的长度,避免重叠匹配。
代码示例(Python)
def compute_prefix_function(pattern):
    m = len(pattern)
    next = [0] * m
    j = 0
    
    for i in range(1, m):
        while j > 0 and pattern[i] != pattern[j]:
            j = next[j - 1]
        if pattern[i] == pattern[j]:
            j += 1
        next[i] = j
    
    return next

def kmp_count_nonoverlapping(text, pattern):
    n = len(text)
    m = len(pattern)
    next = compute_prefix_function(pattern)
    j = 0
    
    count = 0
    i = 0
    
    while i < n:
        while j > 0 and text[i] != pattern[j]:
            j = next[j - 1]
        if text[i] == pattern[j]:
            j += 1
        if j == m:
            count += 1
            j = 0  # 重置为0,确保不重叠匹配
            i -= 1  # 使得i继续从匹配后的下一个字符开始
        i += 1
    
    return count

while True:
    try:
        pattern = input().strip()
        if pattern == "#":  # 题目中特殊标识结束
            break
        text = input().strip()
        print(kmp_count_nonoverlapping(text, pattern))
    except EOFError:
        break

4. 总结

  • P1308:字符串匹配统计,主要处理大小写和分词。
  • P3375:使用KMP算法解决模式字符串匹配问题,输出所有匹配位置。
  • HDU2087:KMP算法统计不重叠匹配次数,避免重复匹配。

三、二叉查找树

  • POJ1577、POJ2309,POJ2418、HDU3791

1. POJ1577 - Periodic Strings

题目描述

给定一个字符串,求该字符串的最小周期长度。周期长度是指该字符串可以通过重复一个子串得到。

解题思路
  1. 周期性检测:一个字符串的最小周期长度为字符串长度减去字符串的前缀函数最后一个值,即 n - next[n-1]
  2. KMP算法的前缀函数:使用KMP算法中的前缀函数表 next 来求解这个问题。
代码示例(Python)
def compute_prefix_function(pattern):
    m = len(pattern)
    next = [0] * m
    j = 0
    
    for i in range(1, m):
        while j > 0 and pattern[i] != pattern[j]:
            j = next[j - 1]
        if pattern[i] == pattern[j]:
            j += 1
        next[i] = j
    
    return next

def find_min_period_length(s):
    n = len(s)
    next = compute_prefix_function(s)
    return n - next[-1]

# 输入处理
while True:
    s = input().strip()
    if s == ".":
        break
    print(len(s) // find_min_period_length(s))

2. POJ2309 - Optimal Milking

题目描述

给定一些奶牛在不同时间产奶的收益,选择若干天进行产奶以获得最大收益,同时需要考虑特定的约束条件(例如某些天之间的收益不可同时选择)。

解题思路
  1. 动态规划:将问题转化为动态规划问题,定义 dp[i] 表示前 i 天的最大收益。对于每一天,选择是否进行产奶。
  2. 根据是否产奶,状态转移方程为 dp[i] = max(dp[i-1], dp[j-1] + profit[i]),其中 j 是可以选择的上一次没有冲突的天。
代码示例(Python)
def max_milking_profit(profits):
    n = len(profits)
    dp = [0] * (n + 1)
    
    for i in range(1, n + 1):
        dp[i] = max(dp[i-1], dp[i-2] + profits[i-1])
    
    return dp[n]

# 示例输入
profits = [int(x) for x in input().split()]
print(max_milking_profit(profits))

3. POJ2418 - Frequent Words

题目描述

给定一篇文章中的所有单词,统计每个单词出现的频率,并按字典序输出每个单词及其频率。

解题思路
  1. 哈希表:使用哈希表(如 Python 的 collections.Counter)来统计每个单词的出现次数。
  2. 排序:按字典序输出单词及其频率。
代码示例(Python)
from collections import Counter

def count_word_frequencies(words):
    word_count = Counter(words)
    sorted_words = sorted(word_count.items())
    
    for word, count in sorted_words:
        print(f"{word} {count/len(words):.4f}")

# 输入处理
words = []
try:
    while True:
        line = input().strip()
        if line == "":
            break
        words.extend(line.split())
except EOFError:
    pass

count_word_frequencies(words)

4. HDU3791 - Treasure Hunter

题目描述

给定一个二维网格地图,其中有障碍物和宝藏,求从起点到终点的最短路径,并计算沿途经过的宝藏数量。

解题思路
  1. BFS(广度优先搜索):从起点开始,使用BFS遍历整个地图,同时记录当前路径的步数和已获取的宝藏数量。
  2. 状态记录:使用一个三维数组记录在某一位置和某个步数情况下可以获得的最大宝藏数量,以避免重复计算。
代码示例(Python)
from collections import deque

def bfs_treasure_hunter(grid, start, end):
    n, m = len(grid), len(grid[0])
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    
    # BFS 队列
    queue = deque([(start[0], start[1], 0, 0)])  # x, y, steps, treasures
    visited = set((start[0], start[1], 0))
    
    while queue:
        x, y, steps, treasures = queue.popleft()
        
        if (x, y) == end:
            return steps, treasures
        
        for dx, dy in directions:
            nx, ny = x + dx, y + dy
            if 0 <= nx < n and 0 <= ny < m and grid[nx][ny] != '#':
                new_treasures = treasures + (1 if grid[nx][ny] == 'T' else 0)
                if (nx, ny, new_treasures) not in visited:
                    visited.add((nx, ny, new_treasures))
                    queue.append((nx, ny, steps + 1, new_treasures))
    
    return -1, 0  # 无法到达终点

# 示例输入
grid = [
    "....T",
    ".#...",
    "S....",
    "...T."
]
start = (2, 0)
end = (0, 4)

steps, treasures = bfs_treasure_hunter(grid, start, end)
print(f"Steps: {steps}, Treasures: {treasures}")

5. 总结

  • POJ1577:使用 KMP 算法的前缀函数求解最小周期问题。
  • POJ2309:通过动态规划求解最优收益问题。
  • POJ2418:使用哈希表统计单词频率,并按字典序输出结果。
  • HDU3791:通过 BFS 搜索最短路径,同时记录沿途的宝藏数量。

四、平衡二叉树

  • TOJ3374、POJ3481、POJ1442,POJ2418

1. TOJ3374 - 最小表示法

题目描述

给定一个字符串,求该字符串的最小表示法,即在所有可能的循环移位中,字典序最小的那个。

解题思路
  1. 最小表示法算法:最小表示法是通过在字符串的所有循环移位中找到字典序最小的那个。这个问题可以通过一个线性算法解决。
  2. 倍增字符串:将字符串拼接为两个相同的字符串,这样就可以模拟所有的循环移位。
  3. 双指针法:使用两个指针 ij 来遍历字符串,比较当前字典序,选择字典序较小的起点作为最终答案。
代码示例(Python)
def minimum_representation(s):
    s = s + s
    n = len(s) // 2
    i, j, k = 0, 1, 0
    
    while i < n and j < n and k < n:
        if s[i + k] == s[j + k]:
            k += 1
        elif s[i + k] > s[j + k]:
            i += k + 1
            if i == j:
                i += 1
            k = 0
        else:
            j += k + 1
            if i == j:
                j += 1
            k = 0
    
    return min(i, j)

# 示例输入
s = "abcab"
start_idx = minimum_representation(s)
print("The smallest representation starts at index:", start_idx)
print("Smallest lexicographical rotation:", s[start_idx:] + s[:start_idx])

2. POJ3481 - Crossing River

题目描述

在给定的时间表和船只的能力下,求渡船运输问题的最短时间。

解题思路
  1. 贪心算法:可以使用贪心策略,每次选择能够带走最多乘客的船只,或者采用二分搜索法来确定一个最小时间。
  2. 状态转移:记录船只可以处理的乘客数量,求出所有乘客被安全送达对岸的最小时间。
代码示例(Python)
def crossing_river(n, m, t, capacities):
    # 贪心算法或二分搜索实现,代码示例为简化版
    capacities.sort(reverse=True)
    total_time = 0
    
    while n > 0:
        n -= capacities[0]  # 使用容量最大的船只先处理乘客
        total_time += t
    
    return total_time

# 示例输入
n = 10  # 乘客数量
m = 2   # 船只数量
t = 5   # 单次渡河时间
capacities = [6, 4]  # 每艘船的容量
print("Minimum time to cross the river:", crossing_river(n, m, t, capacities))

3. POJ1442 - Power Network

题目描述

给定一个电网系统,求其最大流量。电网包括发电站、工厂和供电网络。

解题思路
  1. 最大流问题:使用网络流算法解决问题,如Edmonds-Karp算法或Dinic算法。
  2. 图建模:将电网系统建模为一个流网络,其中发电站作为源点,工厂作为汇点,供电网络作为边,容量为供电能力。
代码示例(Python,使用 Edmonds-Karp 算法)
from collections import deque

def bfs(capacity, flow, source, sink, parent):
    visited = [False] * len(capacity)
    queue = deque([source])
    visited[source] = True
    
    while queue:
        u = queue.popleft()
        
        for v in range(len(capacity)):
            if not visited[v] and capacity[u][v] - flow[u][v] > 0:
                parent[v] = u
                visited[v] = True
                if v == sink:
                    return True
                queue.append(v)
    
    return False

def edmonds_karp(capacity, source, sink):
    n = len(capacity)
    flow = [[0] * n for _ in range(n)]
    parent = [-1] * n
    max_flow = 0
    
    while bfs(capacity, flow, source, sink, parent):
        path_flow = float('Inf')
        s = sink
        
        while s != source:
            path_flow = min(path_flow, capacity[parent[s]][s] - flow[parent[s]][s])
            s = parent[s]
        
        max_flow += path_flow
        v = sink
        
        while v != source:
            u = parent[v]
            flow[u][v] += path_flow
            flow[v][u] -= path_flow
            v = parent[v]
    
    return max_flow

# 示例输入
capacity = [
    [0, 10, 10, 0, 0],
    [0, 0, 2, 4, 8],
    [0, 0, 0, 8, 9],
    [0, 0, 0, 0, 10],
    [0, 0, 0, 0, 0]
]
source = 0
sink = 4
print("Maximum flow:", edmonds_karp(capacity, source, sink))

4. POJ2418 - Frequent Words

题目描述

给定一篇文章中的所有单词,统计每个单词出现的频率,并按字典序输出每个单词及其频率。

解题思路
  1. 哈希表:使用哈希表(如 Python 的 collections.Counter)来统计每个单词的出现次数。
  2. 排序:按字典序输出单词及其频率。
代码示例(Python)
from collections import Counter

def count_word_frequencies(words):
    word_count = Counter(words)
    sorted_words = sorted(word_count.items())
    
    for word, count in sorted_words:
        print(f"{word} {count/len(words):.4f}")

# 输入处理
words = []
try:
    while True:
        line = input().strip()
        if line == "":
            break
        words.extend(line.split())
except EOFError:
    pass

count_word_frequencies(words)

5. 总结

  • TOJ3374:最小表示法问题,可以通过双指针法解决。
  • POJ3481:渡河问题可以通过贪心算法或者动态规划求解。
  • POJ1442:使用网络流算法(如 Edmonds-Karp)求解最大流问题。
  • POJ2418:使用哈希表统计词频,并按字典序输出结果。

你可能感兴趣的:(算法,哈希算法)