juejin算法题_10月27

https://juejin.cn/problemset

小R正在研究DNA序列,他需要一个函数来计算将一个受损DNA序列(dna1)转换成一个未受损序列(dna2)所需的最少编辑步骤。编辑步骤包括:增加一个碱基、删除一个碱基或替换一个碱基。

测试样例
样例1:

输入:dna1 = "AGT",dna2 = "AGCT"
输出:1

样例2:

输入:dna1 = "AACCGGTT",dna2 = "AACCTTGG"
输出:4


-------------------------------------------------
思路:动态规划,划分成子问题。
a[i][j]代表dna1前i个转化成dna2前j个序列所需要的编辑次数,有
	a[i][j]=a[i-1][j-1]    (dna1[i]==dna2[j]的情况下)
	a[i][j]=min(a[i-1][j],a[i][j-1],a[i-1][j-1])+1           (dna1[i]!=dna2[j]的情况下)
import sys
import re

    
def edit_distance(dna1, dna2):
    dp = [[0 for _ in range(len(dna2) + 1)] for _ in range(len(dna1) + 1)]
    
    for i in range(1, len(dp)):
        dp[i][0] = i
    for j in range(1, len(dp[0])):
        dp[0][j] = j
    
    for i in range(1, len(dp)):
        for j in range(1, len(dp[0])):
            if dna1[i-1] == dna2[j-1]:
                dp[i][j] = dp[i-1][j-1]
            else:
                dp[i][j] = min(dp[i-1][j] + 1, dp[i][j-1] + 1, dp[i-1][j-1] + 1)  
    
    return dp[-1][-1]

def parse_dna(input_str):
    matches = re.findall(r'(\w+)\s*=\s*"([^"]+)"', input_str)
    return matches[0][1],matches[1][1]

if __name__ == "__main__":
    for line in sys.stdin:
        dna1,dna2 = parse_dna(line)
        print(edit_distance(dna1,dna2))

小F正在进行一个 AB 实验,需要从整数位置 x 移动到整数位置 y。每一步可以将当前位置增加或减少,且每步的增加或减少的值必须是连续的整数(即每步的移动范围是上一步的 -1,+0 或 +1)。首末两步的步长必须是 1。求从 x 到 y 的最少步数。

输入描述
输入包含两个整数 x 和 y,表示起始位置和目标位置。

输出描述
输出从 x 到 y 所需的最小步数。

测试样例
样例1:

输入:x_position = 12, y_position = 6
输出:4

--------------------------------------
思路:数论。找规律
根据x和y的距离,可以得到1,2出现一次、再3,4出现两次、再5,6出现三次、再7,8出现四次...
距离1,2,3,4,5,6,7,8,9...
步数1,2,3,3,4,4,5,5,5,6,6,6...
import sys
import re

def solution(x1,x2):
    dis = abs(x1-x2)
    if dis==0:
        return 0
    k=1
    p=0
    num=0
    y=0
    while(y<dis):
        y+=k
        p+=1
        num+=1
        if(p==2):
            p=0
            k+=1
    return num
    
def parse_num(input_str):
    matches = re.findall(r'(\w+)\s*=\s*(-*[0-9]+)', input_str)
    return matches[0][1],matches[1][1]

if __name__ == "__main__":
    for line in sys.stdin:
        x1,x2 = parse_num(line)
        x1 = int(x1)
        x2 = int(x2)
        print(solution(x1,x2))

小F得到了一个特殊的字符串,这个字符串只包含字符A、S、D、F,其长度总是4的倍数。他的任务是通过尽可能少的替换,使得A、S、D、F这四个字符在字符串中出现的频次相等。求出实现这一条件的最小子串长度。

测试样例
样例1:

输入:input = "ADDF"
输出:1

样例2:

输入:input = "ASAFASAFADDD"
输出:3

样例3:

输入:input = "SSDDFFFFAAAS"
输出:1

--------------------------------------------------
思路:爆搜。暴力枚举每个区间,长度为n就枚举n*(n+1)/2次
统计这个区间左边和右边区间里,ASDF这四个字母分别出现的总次数,出现总次数都小于len(字符串长度)/4就是一个解。从所有可能的解中取最小值即可
import sys
import re

def solution(str1):
    lf = [[0 for _ in range(len(str1)+2)] for _ in range(4)]
    rt = [[0 for _ in range(len(str1)+2)] for _ in range(4)]
    for i in range(len(str1)):
        lf[0][i+1]=lf[0][i]
        lf[1][i+1]=lf[1][i]
        lf[2][i+1]=lf[2][i]
        lf[3][i+1]=lf[3][i]
        if(str1[i]=='A'):
            lf[0][i+1]+=1
        if(str1[i]=='S'):
            lf[1][i+1]+=1
        if(str1[i]=='D'):
            lf[2][i+1]+=1
        if(str1[i]=='F'):
            lf[3][i+1]+=1
    for i in range(len(str1)-1,-1,-1):
        rt[0][i+1]=rt[0][i+2]
        rt[1][i+1]=rt[1][i+2]
        rt[2][i+1]=rt[2][i+2]
        rt[3][i+1]=rt[3][i+2]
        if(str1[i]=='A'):
            rt[0][i+1]+=1
        if(str1[i]=='S'):
            rt[1][i+1]+=1
        if(str1[i]=='D'):
            rt[2][i+1]+=1
        if(str1[i]=='F'):
            rt[3][i+1]+=1
    
    
    
    res = len(str1)
    length = len(str1)/4
    for L in range(1,len(str1)+1):
        for R in range(L-1,len(str1)+1):
            x1 = lf[0][L-1]+rt[0][R+1]
            x2 = lf[1][L-1]+rt[1][R+1]
            x3 = lf[2][L-1]+rt[2][R+1]
            x4 = lf[3][L-1]+rt[3][R+1]
            if(x1<=length and x2<=length and x3<=length and x4<=length):
                res = min(res,R-L+1)
    return res

if __name__ == "__main__":
    str1 = "AAAADDDDAAAASSSS"
    print(solution(str1))

小R面对一个问题,她有两个由数字字符组成的超大字符串数,需要求出这两个数相加后得到的字符串数中的最大数和最小数之间的位数差距。如果结果中所有数字都相同,则差距为 0。如果存在多个符合最大或最小条件的数,应该选择最小的位置差。

例如,字符串数 "111" 和 "222" 相加得到 "333",所有数字相同,因此位数差为 0。另一例子,字符串数 "111" 和 "34" 相加得到 "145",其中最大数是 '5' 位于第 3 位,最小数是 '1' 位于第 1 位,他们之间的位差为 1。

测试样例
样例1:

输入:string1 = "111",string2 = "222"
输出:0

样例2:

输入:string1 = "111",string2 = "34"
输出:1

样例3:

输入:string1 = "999",string2 = "1"
输出:0
---------------------------------------------
思路:爆搜。
有最大值的索引和有最小值的索引各自用列表存放起来,用二重for循环枚举取最小值
import sys
import re

def solution(s1,s2):
    s1 = list(map(int,s1))
    s2 = list(map(int,s2))
    s3 = add(s1,s2)
    maxx=s3[0]
    idx_maxx=[0]
    minn=s3[0]
    idx_minn=[0]
    for idx,i in enumerate(s3):
        if(maxx<i):
            maxx = i
            idx_maxx = [idx]
        elif (maxx==i):
            idx_maxx.append(idx)
        
        if(minn>i):
            minn = i
            idx_minn = [idx]
        elif(minn==i):
            idx_minn.append(idx)
    
    x = len(s3)
    for i in idx_maxx:
        for j in idx_minn:
            x = min(x,abs(i-j))
    
    if(x==0):
        return 0
    return x-1
    
    
def add(s1, s2):

    length = max(len(s1), len(s2))
    s1.reverse()
    s2.reverse()
    while length - len(s1) > 0:
        s1.append(0)
    while length - len(s2) > 0:
        s2.append(0)
    s3 = []
    t = 0
    for i in range(length):
        if s1[i] + s2[i] + t < 10:
            s3.append(s1[i] + s2[i] + t)
            t = 0
        else:
            s3.append((s1[i] + s2[i] + t) % 10)
            t = 1
    if t == 1:
        s3.append(1)
    s3.reverse()

    return s3

if __name__ == "__main__":
    s1 = "1"
    s2 = "22222"
    print(solution(s1,s2))
n 个整数两两相加可以得到 n(n - 1) / 2 个和。我们的目标是:根据这些和找出原来的 n 个整数。

按非降序排序返回这 n 个数,如果无解,输出 "Impossible"。

测试样例
样例1:

输入:n = 3, sums = [1269, 1160, 1663]
输出:"383 777 886"

样例2:

输入:n = 3, sums = [1, 1, 1]
输出:"Impossible"

样例3:

输入:n = 5, sums = [226, 223, 225, 224, 227, 229, 228, 226, 225, 227]
输出:"111 112 113 114 115"
--------------------------------------------------
思路:数论。
首先, 这n(n - 1) / 2个数中可能有负数,那么就先把所有的数加上个偏置变成非负数,最后再把偏置减回去。
把这n(n - 1) / 2个数都变成非负数有个好处,就是可以确定加上偏置后的长度为n(n - 1) / 2序列,的长度为n的原序列中,所有的数都是非负数。(因为a0+a1>=0,且a0<a1,所以只有x0可能为负数,但是x0是负数的时候,比如[-1,2,3,4],和[0,1,2,5]是等价的,也就等价于x0是非负数)
现在问题就变成了给定n(n - 1) / 2个非负数,求原来的n个非负数。
从0到a0//2枚举x0,再判断x0确定的情况下,能不能从a0,a1,...中分出x1,x2,...。因为x0确定了,又知道a0,那就能算出x1。找到x1了,就能找到x1+x0并把这个数从数列中删除。接下来找到的最小的数一定是x2+x0,再把这个数减去x0后就确定了x2。找到x2了,就能找到x2+x0,x2+x1并把这两个数从数列中删除。以此类推找到所有的数。
import sys
import re

def f(u,n,sums):
    a=[]
    a.append(u)
    for i in range(1,n):
        xx = sums[0]-a[0]
        for x in a:
            if x+xx not in sums:
                return 0,[]
            sums.remove(x+xx)
        a.append(xx)
    return 1,a

def solution(n,sums):
    sums.sort()
    bias = 0
    if(sums[0]<0):
        bias = -sums[0]
        if(bias%2==1):
            bias+=1
        sums = [i+bias for i in sums]
    fl=0
    a=[]
    # print(sums)
    for i in range(0,sums[0]//2+1):
        P=list(sums)
        fl,a=f(i,n,P)
        if(fl==1):
            break
    
    if(fl==0):
        return "Impossible"
    else:
        a = [i-bias//2 for i in a]
        return ' '.join(map(str, a))
    


if __name__ == "__main__":#sb出题人,你管这叫中等?
    #  You can add more test cases here
    print(solution(5, [-1, 0, -1, -2, 1, 0, -1, 1, 0, -1]))
    # print(solution(3, [1, 1, 1]) == "Impossible")
    # print(solution(5, [226, 223, 225, 224, 227, 229, 228, 226, 225, 227]) == "111 112 113 114 115")
    # print(solution(5, [-1, 0, -1, -2, 1, 0, -1, 1, 0, -1]) == "-1 -1 0 0 1")
    # print(solution(5, [79950, 79936, 79942, 79962, 79954, 79972, 79960, 79968, 79924, 79932]) == "39953 39971 39979 39983 39989")
posted @ 2024-10-27 19:30  zhuangzhongxu  阅读(146)  评论(0)    收藏  举报