第313周赛总结与Trie

目录

1.周赛总结

 第1题:公因子的数目

 第2题:沙漏的最大总和

 第3题:最小XOR

 第4题:对字母串可执行的最大删除数

2.Trie

1.周赛总结

首先,先展示一波,我这周的战绩。与第一相比,我简直菜得一批,而且这还是我做得最好的一场周赛!

 第1题:公因子的数目

class Solution:
    def commonFactors(self, a: int, b: int) -> int:
        x = gcd(a,b)
        count = 0
        for i in range(1,x+1):
            if a % i == 0 and b % i == 0:
                count += 1
        return count

没啥好说的,正常的签到题,正常暴力枚举就好!

需要注意的一点是,两个数的公因子,也一定是最大公约数的因子! 

我的代码还可以优化一下!

class Solution:
    def commonFactors(self, a: int, b: int) -> int:
        x = gcd(a,b)
        count = 0
        for i in range(1,x+1):
            if x % i == 0:
                count += 1
        return count

第2题:沙漏的最大总和

这个题也没啥好说的,不必用前缀和啥的,就直接暴力模拟一下即可!

class Solution:
    def maxSum(self, grid: List[List[int]]) -> int:
        def sum_(x,y):
            sum_1 = 0
            for i in range(x,x+3):
                for j in range(y,y+3):
                    if (i == x + 1 and j == y) or (i == x + 1 and j == y +2) :
                        continue
                    sum_1 += grid[i][j]
            return sum_1
        m = len(grid)
        n = len(grid[0])
        res = 0
        for i in range(m - 2):
            for j in range(n - 2):
                res = max(res,sum_(i,j))
        return res

 第3题:最小XOR

这个题思路其实很好想的,不过我模拟时,由于粗心大意,模拟错了三遍!

这道题真得很好,把我最近学得位运算都运用进去了!

class Solution:
    def minimizeXor(self, num1: int, num2: int) -> int:
        def count_(num):
            count = 0
            while num:
                res = num & -num
                num -= res
                count += 1
            return count
        n = count_(num2)
        
        lst=[]
        count = 0
    
        for i in range(31,-1,-1):
            if num1 >> i & 1:
                count += 1
            if count:
                lst.append(num1 >> i & 1)
        lst = lst[::-1]
        # print(n)
        # print(lst)
        l = len(lst)
        if n == count:
            return num1      
        elif n > count:
   
            flag = n - count
            s = 0
            while flag > 0:
                for i in range(l):
                    if flag == 0:
                        break
                    if lst[i] == 0:
                        s += 2 **i
                        # print(s)
                        flag -= 1
                for i in range(l,l+flag):
                    s += 2**i
                    flag -= 1                 
            return num1+s
        elif n < count:
            flag = count - n
            for i in range(l):
                if flag == 0:
                    break
                if lst[i] == 1:
                    num1 -= 2**i
                    flag -= 1
            return num1
                

第4题:对字母串可执行的最大删除数

这道题我不会做!

我就简要说一下,我关注了两个up主的做法!

第一个是灵神:

LCP + 动态规划

LCP:最小公共前缀

求法:

求的是s[i:] 与 s[j:]  的最长公共前缀

s = "abababababsbdbuidhq"

if s[i] == s[j]:

        lcp [i][j] = lcp[i+1][j+1] + 1

else:

        lcp[i][j] = 0   (因为第一个数就不同,那么肯定为0)

动态规划:

if s[i:j] == s[i+j:i+2j]:

        f[i] = f[i + j] + 1   

(把s[i:j]删掉需要一次,之后是之前已经求过的f[i+j],因为是前面与后面有关,所以从后往前遍历)

else:

        f[i] = 1   (不能与后面的数组成公共的,则将其整个删掉,所以为1)

class Solution:
    def deleteString(self, s: str) -> int:     
        n = len(s)
        if len(set(s)) == 1:
            return n

        lcp = [[0] * (n + 1) for _ in range(n + 1)]
        for i in range(n - 1, -1, -1):
            for j in range(n - 1, -1, -1):
                if s[i] == s[j]:
                    lcp[i][j] = lcp[i + 1][j + 1] + 1

        f = [1] * n
        for i in range(n - 1, -1, -1):
            # i + 2j <= n
            # j <= (n - i)//2
            for j in range(1, (n - i) // 2 + 1):
                if lcp[i][i + j] >= j:
                    f[i] = max(f[i], f[i + j]+1)

        return f[0]

第二个是y总:

字符串哈希 + 动态规划

class Solution:
    def deleteString(self, s: str) -> int:
        n = len(s)
        if len(set(s)) == 1:
            return n
        base = 131
        p = [0 for _ in range(n+1)]
        h = [0 for _ in range(n+1)]
        p[0] = 1
        for i in range(1,n+1):
            p[i] = p[i-1] * base
            h[i] = h[i-1] * base + ord(s[i-1])
        def get(l,r):
            return h[r] - h[l-1] * p[r - l + 1]
        f = [1] * n
        for i in range(n - 1, -1, -1):
            for j in range(1, (n - i) // 2 + 1):
                if get(i+1,i+j) == get(i+j+1,i+2*j):
                    f[i] = max(f[i], f[i + j]+1)
        return f[0]

不过python会超时,y总用c++就没超,不过还是贴一下代码,字符串哈希确实巧妙!

class Solution:
    def deleteString(self, s: str) -> int:
        n = len(s)
        if len(set(s)) == 1:
            return n
        base = 131
        p = [0 for _ in range(n+1)]
        h = [0 for _ in range(n+1)]
        MOD = 123456789
        p[0] = 1
        for i in range(1,n+1):
            p[i] = p[i-1] * base % MOD
            h[i] = ((h[i-1] * base) + ord(s[i-1]))% MOD
        def get(l,r):
            return (h[r] % MOD - h[l - 1] * p[r - l + 1] % MOD + MOD) % MOD
        f = [1] * n
        for i in range(n - 1, -1, -1):
            for j in range(1, (n - i) // 2 + 1):
                if get(i+1,i+j) == get(i+j+1,i+2*j):
                    f[i] = max(f[i], f[i + j]+1)
        return f[0]

经过取模优化,这里竟让过了,字符串哈希你太牛了

这里不仅要感叹一下python切片功能的强大,直接用切片比较字符串是否相等,居然是最快的做法,实在令人感叹!

class Solution:
    def deleteString(self, s: str) -> int:
        
        n = len(s)
        if len(set(s)) == 1:
            return n
        f = [1] * n
        for i in range(n - 1, -1, -1):
            # i + 2j <= n
            # j <= (n - i)//2
            for j in range(1, (n - i) // 2 + 1):
                if s[i:i+j] == s[i+j:i+2*j]:
                    f[i] = max(f[i], f[i + j]+1)
        return f[0]

哈哈哈,简单干脆,不得不说,python真强,人生苦短,我用python!


2.Trie

Trie 是一种数据结构,也叫字典树,可以快速存储与查找字符串。

速度很快,甚至比哈希表更快,不过这是一种以时间换空间的结构,需要定义极大的空间。

这里尝试以二维数组方式实现字典树,其实有很多种字典树,不过以我目前的水平,先掌握这一种

吧!来日方长!

如图所示!

所有的字符串,都是从根节点root引入!并且对最后一个节点进行标记,证明存在这样一个以标记节点结尾的字符串!

话不多说,模板代码展示!

trie = [[0 for _ in range(26)]for _ in range(100010)]
cnt =[0 for _ in range(100010)]
idx = 0

def insert(str):
    global trie,cnt,idx
    p = 0
    n = len(str)
    for i in range(n):
        s = ord(str[i]) - 97
        if not trie[p][s]:
            idx += 1
            trie[p][s] = idx
        p = trie[p][s]
    cnt[p] += 1

def search(str):
    global trie,cnt
    p = 0
    n = len(str)
    for i in range(n):
        s = ord(str[i]) - 97
        if not trie[p][s]:
            return 0
        p = trie[p][s]
    return cnt[p]

或许大家不知道,这个二维数组最后变成什么样了,我打印了一部分如下:

再配一道ACwing经典的例题 

结合上述模板,再配上输入输出

n = int(input())
while n:
    o,s = input().split()
    if o =="I":
        insert(s)
    else:
        print(search(s))
    n -= 1

        

除此之外,还有一道变题!

这道题,若用暴力法,时间复杂度是O(n^2),max(N)^2 = 10 ^10,必然超时,于是我们想到在内层循环进行优化!

我们知道 0 ^ 0 = 0, 1 ^ 1 = 0,1 ^ 0 = 1

若想让异或值最大,则要使两个数的二进制数的对应位不一样的多一些。一个贪心的思路

于是,我们想到,将整数转化成二进制数,存储在字典树中,之后在字典树中,不断寻找与自己不一样的节点,便可使得异或值越大!

代码展示如下!

t = [[0,0] for _ in range(3100000)]
idx = 0
def insert(x):
    global t,idx
    p = 0
    for i in range(31,-1,-1):
        u = x >> i & 1
        if not t[p][u]:
            idx += 1
            t[p][u] = idx
        p = t[p][u]
def search(x):
    global t
    p = 0
    res = 0
    for i in range(31,-1,-1):
        u = x >> i & 1
        if t[p][u^1]:
            p = t[p][u^1]
            res = res * 2 + u^1
        else:
            p = t[p][u]
            res = res * 2 + u
    return res
res = 0    
n = int(input())
lst = list(map(int,input().split()))
for x in lst:
    insert(x)
    res = max(res,search(x)^x)
print(res)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值