最长回文子串-中心扩散法(力扣题号5)

背景:这种方法其实很特殊,目前看只有回文子串用得上,反正也是一种思路,这里就先记录一下,这里会写出详细的写程序-改程序过程,那种题解上来就给个结果,再回头拆分,我不信谁都是这么一步到位的写法

1、先考虑特殊情况

我是个测试开发,所以每次都会把一些特殊情况单独列出来,也叫无效/特殊等价类

这种题目的特殊等价类有这么几种

  • 字符串为空(和下面重复了)
  • 字符串长度为1(和下面也重复了,但这是一种思路,体现思考过程和测试用例设计)
  • 字符串不管多长,本身已经是回文字符串了(我看到一个758长度的测试用例就是这个条件)

所以单独写出这一句判断:

    if s == s[::-1]:
        return s

2、再来找规律

然后再来判断长度为2以上的字符子串,从第二个字符到倒数第二个字符分别有多长(后续优化从第一个到最后这么判断,因为“ac”应该返回“a”,而不是“c”)

这种扩散分为两种情况,len(sub_string)为奇数以及偶数

s = "acbcddcab"

1)奇数情况下

就从s[2]='b'开始吧,肯定2,123,01234……这种模式走下去的

对应字符串就是b,cbc,acbcd,到这里就失败了,所以s[2]为中心的最长子串“半径就是1,我准备用一个中心和一个半径记录最大值,那么num[2]就等于1,输出这个字符串的话就是s[中心-半径:中心+半径+1]=s[1,4]="cbc",这里+1是因为切片操作含左不含右

2)偶数情况下

就从s[4]+s[5]="dd"开始,然后45,3456,234567……

对应到dd,cddc,bcddca,到这里失败,这里就有一个问题,如果还是中心和半径来记录的话,这个子串的中心在哪里,4.5么?半径肯定还是个整数,这里应该是2,所以为了节约存储空间的话,还是应该找到一个统一的模式,来记录这两种遍历方式

① 中心+半径

这种方式对于奇数来说,中心为3,半径为1,长度为3,输出结果应该是s[2]+s[3]+s[4]

这种方式对于奇数来说,中心为3.5,半径为2,长度为4,输出结果应该是s[2]+s[3]+s[4]+s[5]

② 起点+长度

其实上面一种方式也能看出来,起点就是中心-半径,长度就是半径的2倍,这种方式其实可以比较好的统一两种模式,虽然中心3.5也可以转int运算,但是最终选择起点+长度这种存储,比较通用,连暴力破解也是这种存储方式

3、下面是题解的代码

def longestPalindrome(self, s):
        # 先排除异常情况,就是上面提到的几种
        if s == s[::-1]:
            return s

        # 定义ans存储最长的子串
        ans = ""
        for mid in range(len(s)):
            # 把单双两种情况都跑一遍,函数用的是同一个,但是根据参数调用不同来区分
            if len(central(mid, mid, s)) > len(ans):
                ans = central(mid, mid, s)
                
            if len(central(mid, mid + 1, s)) > len(ans):
                ans = central(mid, mid + 1, s)
        # print(ans)
        return ans


# 这里专门定义一个函数,用来中心拓展,mid1=mid2说明是单数,否则就是双数
def central(mid1, mid2, s):
    begin = 0  # 默认第一个字符开始
    length = 1  # 默认单个字符就是最长回文子串
    
    # 这里可以看到无论单双,都是以自身为中心,左右指针向外拓展,记得第一次就要判断初始值是否相等
    left = mid1
    right = mid2

    while (left > -1 and right < len(s)):
        if s[left] == s[right]:
            left -= 1
            right += 1
            begin = left + 1
            length = right - left
        else:

            break

    # print(begin, length)
    return s[begin:begin + length - 1]

4、最后看一下运行结果

 总结:

这种用法不是很广泛,但是并不难,可以锻炼一下循环退出的条件判断,以及函数传参的调用,也算是很好的思维锻炼

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值