【Leetcode】1147-1912
题目
ps:之后的文章会先把题目链接过来
1147.段式回文<<--
你会得到一个字符串 text 。你应该把它分成 k 个子字符串 $(subtext_1, subtext_2,…, subtext_k) $,要求满足:
\(subtext_i\) 是 非空 字符串
所有子字符串的连接等于text( 即\(subtext_1 + subtext_2 + ... + subtext_k == text\) )
对于所有i的有效值( 即 1 <= i <= k ) ,$subtext_i == subtext_k - i + 1 $均成立
返回k可能最大值。
- \(1\leq text.length\leq 1000\)
- \(text\) 仅由小写英文字符组成
前言
之前做过的题目,现在在做就可以给出一个至少在我看起来更好的代码了
之后也是通过逐步的思考,写出更漂亮和简洁的代码。
思路
题目给定的分割,可以发现一定是首和尾进行对应,那么如果首尾找到了一个符合条件的字符串,那么就可以递归的进行分割。
即,如果我们找到了\(text[:i]==text[-i:]\),这个时候我们就可以递归的进行longestDecomposition(text[i:-i])的调用。
这样我们就找到了递归是如何进行的,此时问题的关键就在于,我们是找到一个i以进行递归还是说需要遍历每种情况之后,选择最优的。
下边进行证明,不需要找出所有的i然后对比选出最优的情况,只需要找到最短或者说最近的符合条件的i即可进行递归。证明如下:
假定在当前所在字符串的头部和尾部分别存在字符串A,B 和 A,B。即整个字符串为AB...AB。
如果在选出A的时候已经找到了符合条件的i,即A==B,此时可以进行递归。由于
A==B,因此如果我们此时选择了A==B,那么后续可以继续选择B==A,这样就将匹配的AB分割为了四个段,而如果我们选择了将AB进行匹配,则得到了2个子字符串,因此找到可以匹配分割的方案总是优于选择后续分割的方案
据此,我们就可以写出最初版的代码
class Solution:
def longestDecomposition(self, s: str) -> int:
if len(s)<=1:return len(s)
for i in range(1,len(s)//2+1):
if s[:i]==s[-i:]:
return 2+self.longestDecomposition(s[i:-i])
return 1
这样的做法是最简单和代码最简洁的,但是可以发现的是,我们每次需要进行字符的切片操作,但是实际上只需要记录一下每次递归需要的字符索引即可。这样就写出了第二个版本的代码:
class Solution:
def longestDecomposition(self, text: str) -> int:
n = len(text)
def dfs(l, r):
if l > r: return 0
# ans = 1
for i in range(1, r - l + 1):
if text[l:l + i] != text[r - i + 1:r + 1]:continue
return 2 + dfs(l + i, r - i)
return 1
return dfs(0, n - 1)
此外,可以发现的是,因为匹配总是左右对称的,因此递归的时候确定了左边界,那么右边界也是直接可以确定的。据此也可以将递归的参数再减少一个。
其中最简单的办法就是在dfs的内部首先计算一下r,然后其余代码均不需要变动,这里就不在赘述。
这列给出一个不写出确定的右边界,而是通过左边界的最大值进行计算的代码
class Solution:
def longestDecomposition(self, text: str) -> int:
N = len(text)
n,f = divmod(N,2)
def dfs(l):
if l>=n+f: return 0
for i in range(1,n-l+1):
if text[l:l+i] != text[N-i-l:N-l]:continue
return 2 + dfs(l+i)
return 1
return dfs(0)

浙公网安备 33010602011771号