基本字符串转换 | 豆包MarsCode AI刷题

97 阅读3分钟

问题描述

小U正在研究字符串转换问题。给定一个长度为N的字符串S,每次操作可以选择三个索引i、j、k(i < j < k),如果S[i]和S[k]都是'a',则可以将S[j]设置为'a'。判断是否可以通过多次操作将S转换为全'a'字符串。

问题解决

首先我向豆包请教解题思路。豆包指出这是一个字符串转换问题,关键是找到转换的规律。每次操作需要两个'a'才能产生一个新的'a',这意味着我们需要从已有的'a'开始,逐步扩展。实际上这是一个贪心的过程,应该尽可能利用已有的'a'来转换更多位置。

根据这个思路,我写出了第一版代码:

def solution(N: int, S: str) -> bool: 
    is_a = [c == 'a' for c in S]  
    changed = True  
    while changed:  
        changed = False   
        for j in range(1, N-1):  
            if not is_a[j]: 
                for i in range(j):  
                    if not is_a[i]:  
                        continue  
                    for k in range(j+1, N):  
                        if is_a[k]:  
                            is_a[j] = True  
                            changed = True  
                            break  
                    if is_a[j]:  
                        break  
    return 1 if all(is_a) else 0                    

之后我询问豆包我的代码可以如何优化,豆包给出了如下回答:

image.png

豆包发现我的代码的潜在问题还是在于三层嵌套导致时间复杂度过高,以及在找到满足条件的k后就应该直接跳出循环。

基于上述意见我对代码进行了优化:

def solution(N: int, S: str) -> bool:
    is_a = [c == 'a' for c in S]
    while True:
        new_a = is_a.copy()
        changed = False
        for j in range(1, N-1):
            if not is_a[j]:
                left = j - 1
                right = j + 1
                while left >= 0 and not is_a[left]:
                    left -= 1
                while right < N and not is_a[right]:
                    right += 1
                if left >= 0 and right < N and is_a[left] and is_a[right]:
                    new_a[j] = True
                    changed = True
        if not changed:
            break
        is_a = new_a
    return 1 if all(is_a) else 0

这个改进版本相比第一版有几个显著特点:

这个改进版本的代码有几个显著的特点:

  1. 双指针技巧:使用left和right两个指针,分别向左和向右寻找最近的'a',这比之前的遍历方式更高效。
  2. 状态记录:使用布尔数组is_a记录每个位置是否为'a',避免重复字符串操作。
  3. 增量更新:通过new_a复制当前状态,在一轮操作中记录所有可以转换的位置,然后统一更新。
  4. 终止条件:当没有新的位置可以被转换时(changed为False),说明已经达到最终状态。

学习心得

通过这个优化过程,我深刻理解到算法优化的精髓往往在于选择合适的数据结构和遍历方式。最初的解法虽然正确,但效率不高,而改进后的版本通过双指针技巧大大减少了不必要的遍历。

这个问题还告诉我们,有时候看似复杂的问题可以通过合适的方法转化为简单的模式。在这里,我们不需要考虑具体的转换顺序,只需要找到每个位置是否可以被转换即可。这种抽象思维的能力,对解决算法问题至关重要。

最后,代码的可读性和效率之间的平衡也很重要。通过清晰的变量命名和代码结构,我们既保持了代码的可维护性,又实现了性能的优化。这种编程思维和技巧,对今后解决类似问题会有很大帮助。