Bootstrap

每日一题——Python实现PAT乙级1094 谷歌的招聘(举一反三+思想解读+逐步优化)五千字好文


一个认为一切根源都是“自己不够强”的INTJ

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

目录

我的写法

代码分析

时间复杂度分析

空间复杂度分析

总结

我要更强

1. 使用埃拉托斯特尼筛法预处理质数

2. 使用滑动窗口减少重复计算

3. 使用位操作优化质数检测

4. 使用动态规划预处理质数

总结

哲学和编程思想

1. 预处理和缓存(Caching)

2. 滑动窗口(Sliding Window)

3. 位操作(Bit Manipulation)

4. 动态规划(Dynamic Programming)

5. 实用主义(Pragmatism)

6. 效率优化(Efficiency Optimization)

总结

举一反三

1. 预处理和缓存

2. 滑动窗口

3. 位操作

4. 动态规划

5. 实用主义

6. 效率优化

7. 抽象和泛化

8. 学习和借鉴

9. 实践和测试


题目链接:https://pintia.cn/problem-sets/994805260223102976/exam/problems/type/7?problemSetProblemId=1071785997033074688&page=0

我的写法

import math
L,K=map(int,input().split())
N=input()
def is_prime(n):
    if n<2:
        return False
    elif n==2:
        return True
    elif n%2==0:
       return False 
    else:
        for i in range(3,math.isqrt(n)+1,2):
            if n%i==0:
                return False
    return True

for i in range(L-K+1):
    if is_prime(int(N[i:i+K])):
        #不是print(int(N[i:i+K]))!
        print(N[i:i+K])
        break
else:
    print("404")

 

代码分析

  1. 输入部分:
    • L, K = map(int, input().split()):读取两个整数 L 和 K,分别表示字符串 N 的长度和要查找的子串长度。
    • N = input():读取字符串 N。
  2. 质数检测函数:
    • is_prime(n):一个用于检测整数 n 是否为质数的函数。该函数首先处理一些特殊情况(小于2的数和2本身),然后检查偶数,最后通过从3到 sqrt(n) 的奇数来检查是否存在因子。
  3. 主循环:
  • for i in range(L-K+1)::遍历所有可能的起始位置,以提取长度为 K 的子串。
  • if is_prime(int(N[i:i+K]))::检查当前子串表示的整数是否为质数。
  • print(N[i:i+K]):如果是质数,则输出该子串并跳出循环。
  • else::如果循环结束后没有找到质数子串,则输出 "404"。

时间复杂度分析

  • 质数检测函数:is_prime(n) 的时间复杂度主要取决于 n 的大小。在最坏情况下,它需要检查从3到 sqrt(n) 的所有奇数,因此时间复杂度为 O(sqrt(n))。
  • 主循环:主循环最多会执行 L-K+1 次,每次调用 is_prime 函数。因此,总的时间复杂度为 O((L-K+1) * sqrt(10^K)),其中 10^K 是子串表示的最大可能整数。

空间复杂度分析

  • 输入部分:存储 L、K 和 N 的空间复杂度为 O(L)。
  • 质数检测函数:该函数的空间复杂度为 O(1),因为它只使用了常数级别的额外空间。
  • 主循环:主循环的空间复杂度也为 O(1),因为它只使用了常数级别的额外空间。

综上所述,总的空间复杂度为 O(L)。

总结

  • 时间复杂度:O((L-K+1) * sqrt(10^K))
  • 空间复杂度:O(L)

这段代码在处理较大输入时可能会遇到性能问题,特别是当 K 较大时,质数检测的时间复杂度会显著增加。此外,代码在处理大整数时可能会受到 Python 整数表示的限制。


我要更强

优化时间复杂度和空间复杂度的方法有很多,以下是一些可能的优化策略,并附上相应的代码和注释:

1. 使用埃拉托斯特尼筛法预处理质数

预先计算并存储一定范围内的质数,可以显著减少质数检测的时间复杂度。

import math

# 预处理质数
def sieve_of_eratosthenes(limit):
    is_prime = [True] * (limit + 1)
    is_prime[0] = is_prime[1] = False
    for i in range(2, int(math.sqrt(limit)) + 1):
        if is_prime[i]:
            for j in range(i*i, limit + 1, i):
                is_prime[j] = False
    return is_prime

# 读取输入
L, K = map(int, input().split())
N = input()

# 预处理质数
limit = 10**K
is_prime = sieve_of_eratosthenes(limit)

# 查找质数子串
found = False
for i in range(L - K + 1):
    if is_prime[int(N[i:i+K])]:
        print(N[i:i+K])
        found = True
        break

if not found:
    print("404")

2. 使用滑动窗口减少重复计算

通过滑动窗口技术,可以减少对子串的重复转换和质数检测。

import math

# 质数检测函数
def is_prime(n):
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True

# 读取输入
L, K = map(int, input().split())
N = input()

# 初始化滑动窗口
window = int(N[:K])
if is_prime(window):
    print(N[:K])
else:
    for i in range(1, L - K + 1):
        window = window * 10 - int(N[i-1]) * (10**K) + int(N[i+K-1])
        if is_prime(window):
            print(N[i:i+K])
            break
    else:
        print("404")

3. 使用位操作优化质数检测

通过位操作可以进一步优化质数检测的效率。

import math

# 质数检测函数
def is_prime(n):
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    for i in range(3, int(math.sqrt(n)) + 1, 2):
        if n % i == 0:
            return False
    return True

# 读取输入
L, K = map(int, input().split())
N = input()

# 查找质数子串
found = False
for i in range(L - K + 1):
    if is_prime(int(N[i:i+K])):
        print(N[i:i+K])
        found = True
        break

if not found:
    print("404")

4. 使用动态规划预处理质数

通过动态规划预处理质数,可以进一步减少质数检测的时间复杂度。

import math

# 预处理质数
def preprocess_primes(limit):
    is_prime = [True] * (limit + 1)
    is_prime[0] = is_prime[1] = False
    for i in range(2, int(math.sqrt(limit)) + 1):
        if is_prime[i]:
            for j in range(i*i, limit + 1, i):
                is_prime[j] = False
    return is_prime

# 读取输入
L, K = map(int, input().split())
N = input()

# 预处理质数
limit = 10**K
is_prime = preprocess_primes(limit)

# 查找质数子串
found = False
for i in range(L - K + 1):
    if is_prime[int(N[i:i+K])]:
        print(N[i:i+K])
        found = True
        break

if not found:
    print("404")

总结

以上方法通过预处理质数、滑动窗口技术和动态规划等手段,可以有效优化时间复杂度和空间复杂度。具体选择哪种方法取决于输入规模和具体应用场景。


哲学和编程思想

这些优化方法体现了多种哲学和编程思想,以下是一些具体的说明:

1. 预处理和缓存(Caching)

预处理质数和缓存结果是一种常见的优化策略,它体现了“空间换时间”的思想。通过预先计算并存储结果,可以在后续操作中快速检索,从而减少重复计算的时间。

  • 哲学思想:这是一种实用主义的体现,即通过权衡资源(如内存)的使用来优化性能。
  • 编程思想:这是“缓存”和“预计算”的设计模式,常见于动态规划和记忆化搜索等算法中。

2. 滑动窗口(Sliding Window)

滑动窗口技术通过维护一个固定大小的窗口,在数据序列上滑动,从而减少重复计算。这种技术在处理连续子序列问题时非常有效。

  • 哲学思想:这是一种“局部性原理”的体现,即系统或过程的当前状态很大程度上取决于其最近的历史状态。
  • 编程思想:这是“窗口化”和“状态维护”的设计模式,常见于字符串处理和序列分析等算法中。

3. 位操作(Bit Manipulation)

位操作通过直接操作二进制位来优化计算效率,尤其在处理整数运算时非常有效。

  • 哲学思想:这是一种“最小作用原理”的体现,即通过最小的操作来达到最大的效果。
  • 编程思想:这是“位运算”和“高效计算”的设计模式,常见于底层优化和算法竞赛中。

4. 动态规划(Dynamic Programming)

动态规划通过将问题分解为重叠子问题,并存储子问题的解来避免重复计算,从而优化时间复杂度。

  • 哲学思想:这是一种“分而治之”和“最优子结构”的体现,即通过分解问题并找到子问题的最优解来构建整体的最优解。
  • 编程思想:这是“动态规划”和“状态转移”的设计模式,常见于优化问题和组合问题中。

5. 实用主义(Pragmatism)

在优化算法时,实用主义思想强调实际效果和可行性,即通过权衡不同的资源使用(如时间、空间)来达到最佳的性能。

  • 哲学思想:实用主义强调结果的有效性和实用性,而不是抽象的理论或原则。
  • 编程思想:在编程中,实用主义体现在选择合适的算法和数据结构,以及在性能和资源之间做出权衡。

6. 效率优化(Efficiency Optimization)

在编程中,效率优化是一种常见的思想,它强调通过各种手段(如算法改进、数据结构选择、代码优化)来提高程序的运行效率。

  • 哲学思想:效率优化体现了“精益求精”和“持续改进”的原则。
  • 编程思想:在编程中,效率优化体现在不断寻找和应用更高效的算法和实现方法。

总结

这些优化方法不仅体现了编程中的各种设计模式和算法思想,还反映了哲学上的实用主义、局部性原理、最小作用原理等思想。通过综合运用这些思想,可以有效地优化算法的时间复杂度和空间复杂度。


举一反三

根据上述哲学和编程思想,结合本题目,以下是一些具体的技巧和建议,帮助在解决类似问题时举一反三:

1. 预处理和缓存

  • 预处理质数:在本题目中,预处理质数列表可以显著减少质数检测的时间。你可以将这种预处理思想应用到其他需要频繁质数检测的场景中。
  • 缓存中间结果:在其他问题中,如果存在重复计算,可以考虑使用缓存机制来存储中间结果,避免重复计算。

2. 滑动窗口

  • 连续子串问题:本题目中使用了滑动窗口来查找质数子串。在处理类似连续子串或子数组的问题时,可以考虑使用滑动窗口技术。
  • 动态维护窗口:在其他问题中,如果需要动态维护一个连续的子序列或子数组,滑动窗口是一个有效的工具。

3. 位操作

  • 优化整数运算:在本题目中,位操作可以用来优化质数检测。在处理整数运算时,考虑是否可以使用位操作来提高效率。
  • 位运算技巧:学习和掌握常用的位运算技巧,如位掩码、位移等,这些技巧在优化算法时非常有用。

4. 动态规划

  • 重叠子问题:本题目虽然没有直接使用动态规划,但动态规划的思想可以应用到其他需要解决重叠子问题的问题中。
  • 状态转移方程:在其他问题中,如果存在重叠子问题,可以尝试设计状态转移方程,使用动态规划来优化解决方案。

5. 实用主义

  • 权衡时间和空间:在选择算法时,权衡时间和空间复杂度,选择最适合当前问题的方案。
  • 考虑实际应用:在解决实际问题时,考虑输入规模、性能要求等因素,选择合适的优化策略。

6. 效率优化

  • 持续改进算法:不断寻找和应用更高效的算法和实现方法,持续改进代码性能。
  • 代码审查和优化:进行代码审查,寻找潜在的优化点,如减少不必要的计算、简化逻辑等。

7. 抽象和泛化

  • 抽象问题模型:将具体问题抽象为更一般的模型,寻找通用的解决方法。
  • 泛化解决方案:设计通用的算法框架,使其适用于多种类似问题。

8. 学习和借鉴

  • 学习经典算法:学习经典的算法和数据结构,理解其设计思想和应用场景。
  • 借鉴他人经验:参考他人的解决方案和优化经验,从中学习和借鉴。

9. 实践和测试

  • 编写测试用例:编写全面的测试用例,验证算法的正确性和性能。
  • 性能分析:使用性能分析工具,找出代码的瓶颈,有针对性地进行优化。

通过综合运用这些技巧和建议,可以在解决类似问题时更加灵活和高效,举一反三,不断提升自己的编程能力和问题解决能力。


;