【算法训练营】邓老师书,子序列,前缀(python实现)

邓老师数


时间限制:1 sec

空间限制:256 MB

问题描述

众所周知,大于 1 的自然数中,除了 1 与其本身外不再有其他因数的数称作质数(素数)

对于大于 1 的不是质数的自然数,我们又称作合数

参加了邓老师算法训练营的小 Z 突发奇想,定义了新的数:所有合数中,除了 1 与其本身外,其他因数均为质数的数,称作邓老师数

现在,小 Z 给定两个数 n,k,其中 k 的取值为 0 或 1。如果 k=0,小 Z 希望你告诉他所有不超过 n 的质数;如果 k=1,小 Z 希望你告诉他所有不超过 n 的邓老师数。

输入格式

一行两个用空格隔开的整数 n,k,意义见题目描述。

输出格式

对于每个找到的质数或邓老师数,输出一行一个整数表示这个你找到的数。

请升序输出所有答案。

样例输入

9 1

样例输出

4
6
9

样例解释

4 除去 1 与其本身外的因子有 2,均为质数,因此 4 是邓老师数。

6 除去 1 与其本身外的因子有 2,3,均为质数,因此 6 是邓老师数。

9 除去 1 与其本身外的因子有 3,均为质数,因此 9 是邓老师数。

8 除去 1 与其本身外的因子有 2,4,由于 4 不是质数,因此 8 不是邓老师数。

数据范围

本题共设置 8 个测试点,测试点编号从 1 至 8。

对于前 4 个测试点,保证 n<=1,000。

对于编号为偶数的测试点,保证 k=0;对于编号为奇数的测试点,保证 k=1。

对于所有的 8 个测试点,保证 n<=2*10^5。

提示

[对于求质数的问题,可以直接用Eratosthenes筛法求解。]

[对于求邓老师数的问题,考虑Eratosthenes筛法中“筛去”合数的逻辑,是否可以对其略作修改,使之支持筛出“非邓老师数”呢?]

代码实现 

from typing import List

def get_answer(n: int, k: int) -> List[int]:
    is_prime = [True] * (n + 1)
    is_deng = [True] * (n + 1)
    ans = []

    for i in range(2, n + 1):
        if is_prime[i]:
            is_deng[i] = False

        if (k == 0 and is_prime[i]) or (k == 1 and is_deng[i]):
            ans.append(i)

        for j in range(i * 2, n + 1, i):
            is_prime[j] = False
            if not is_prime[j // i]:
                is_deng[j] = False

    return ans

def main():
    n, k = map(int, input().split())
    ans = get_answer(n, k)
    for num in ans:
        print(num)

if __name__ == "__main__":
    main()

子序列


描述

给定一个字符串,求出该字符串有多少不同的子序列。

子序列:字符串中按顺序抽出一些字符得到的串。比如字符串abcd里,ab、ac、ad、abc、acd都是子序列。

输入

输入一个字符串。

输出

输出不同的子序列的个数除以23333得到的余数。

样例1输入

ababc

样例1输出

23

样例1解释

有这些子序列:

a,b,c,aa,ab,ac,ba,bb,bc,aba,abb,abc,aab,aac,bab,bac,bbc,abab,abac,abbc,aabc,babc,ababc

样例2

请查看下发文件内的sample2_input.txt和sample2_output.txt。

限制

对于50%的数据,字符串长度不超过15;

对于100%的数据,字符串长度不超过500000。

字符串为26个小写字母组成。

时间:2 sec

空间:512 MB

代码实现

def get_answer(s: str) -> int:
    n = len(s)
    mo = 23333
    f = [0] * (n + 1)
    p = [0] * (n + 1)
    last = [-1] * 26

    for i in range(n):
        a = ord(s[i]) - ord('a')
        p[i + 1] = last[a]
        last[a] = i + 1

    for i in range(1, n + 1):
        if p[i] == -1:
            f[i] = (2 * f[i - 1] + 1) % mo
        else:
            f[i] = (2 * f[i - 1] - f[p[i] - 1] + mo) % mo

    return f[n]

def main():
    s = input().strip()
    result = get_answer(s)
    print(result)

if __name__ == "__main__":
    main()

前缀


描述

给定n个字符串,再询问m次,每个询问给出一个字符串,求出这个字符串是n个字符串里,多少个串的前缀。

前缀:从头开始的一段连续子串。比如字符串ab是字符串abcd的前缀,也是字符串ab(自身)的前缀,但不是bab的前缀。

输入

第一行包含两个正整数n,m。

接下来n行,每行表示一个字符串,表示给定的n个字符串中的一个。

再接下来m行,每行一个字符串,表示询问的字符串。

输出

输出m行,每行表示询问的答案。

样例1输入

5 4
ab
abc
ab
ba
bb
a
b
ab
abc

样例1输出

3
2
3
1

样例1解释

字符串a是ab、abc、ab的前缀;

字符串b是ba、bb的前缀;

字符串ab是ab、abc、ab的前缀;

字符串abc是abc的前缀。

样例2

请查看下发文件内的sample2_input.txt和sample2_output.txt。

限制

对于50%的数据,n,m ≤ 500;

对于100%的数据,n,m ≤ 5000。

字符串为26个小写字母组成,且单个长度不超过500,n个字符串的长度之和不超过1000000。

时间:10 sec

空间:512 MB

提示

[trie树基本题。]

代码实现 

class TrieNode:
    def __init__(self):
        self.prefix_latter_words = 0
        self.children = [None] * 26

class Trie:
    def __init__(self):
        self.root = TrieNode()

    @staticmethod
    def index(c: str) -> int:
        return ord(c) % 26

    def insert(self, word: str):
        cur = self.root
        for c in word:
            idx = self.index(c)
            if not cur.children[idx]:
                cur.children[idx] = TrieNode()
            cur = cur.children[idx]
            cur.prefix_latter_words += 1

    def count_prefix(self, prefix: str) -> int:
        cur = self.root
        for c in prefix:
            idx = self.index(c)
            if not cur.children[idx]:
                return 0
            cur = cur.children[idx]
        return cur.prefix_latter_words

def main():
    t = Trie()

    n, m = map(int, input().split())
    for _ in range(n):
        word = input().strip()
        t.insert(word)

    for _ in range(m):
        prefix = input().strip()
        print(t.count_prefix(prefix))

if __name__ == "__main__":
    main()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

X.AI666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值