背景:这种方法其实很特殊,目前看只有回文子串用得上,反正也是一种思路,这里就先记录一下,这里会写出详细的写程序-改程序过程,那种题解上来就给个结果,再回头拆分,我不信谁都是这么一步到位的写法
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、最后看一下运行结果
总结:
这种用法不是很广泛,但是并不难,可以锻炼一下循环退出的条件判断,以及函数传参的调用,也算是很好的思维锻炼