https://leetcode.com/problems/substring-with-largest-variance/
算是很巧妙的转换了,没想出来。同时也有更有趣的解法,记下来备忘。
题目分析
乍一看是一道普通的dp题目,但构造不出来。
其实题目中的小写字母,完全可以遍历枚举,于是问题可以进行转化:转化成只有两个字母参与的子串。
统计两个字母组成的字符串,其中差异最大的子串,就是一个经典的MaxSubarraySum问题,也叫Kadane算法,这里就不多说了。
不过,问题并没有解决,题目中还有限制条件,不能只包含最多的那个字母,另一个字母至少出现一次,所以需要做一些变化,一个很直观的变化就是,用一个flag表示有没有出现,如果出现了,那就是正常的MaxSubarraySum,如果没有,那最终的结果需要-1。
这样的解法如下:
class Solution:def largestVariance(self, s: str) -> int:st = set(s)res = 0for x in st:for y in st:if x != y:hasY = Falseacc = 0for c in s:if c != x and c != y:continueif c == x:acc += 1if hasY:res = max(res, acc)else:res = max(res, acc - 1)elif c == y:if acc > 0:hasY = Trueacc -= 1else:hasY = Falseacc = 0return res
还有一个相反的策略,不够直观,但也挺巧妙的,维护左侧累计的最小值,最大值就是 当前累计-左侧最小累计。
里边的一个小trick是,默认上一个左侧累计是0,左侧最小累计初始是inf,只有在遇到y时才更新左侧最小累计,完美解决了只要需要一个y的问题。
class Solution:def largestVariance(self, s: str) -> int:st = set(s)res = 0for x in st:for y in st:if x != y:acc = 0prevDeletedPart = 0deletedPart = 10**5for c in s:if c != x and c != y:continueif c == x:acc += 1elif c == y:deletedPart = min(deletedPart, prevDeletedPart)acc -= 1prevDeletedPart = accres = max(res, acc - deletedPart)return res
