https://leetcode.com/problems/substring-with-largest-variance/
算是很巧妙的转换了,没想出来。同时也有更有趣的解法,记下来备忘。

题目分析

乍一看是一道普通的dp题目,但构造不出来。
其实题目中的小写字母,完全可以遍历枚举,于是问题可以进行转化:转化成只有两个字母参与的子串。
统计两个字母组成的字符串,其中差异最大的子串,就是一个经典的MaxSubarraySum问题,也叫Kadane算法,这里就不多说了。

不过,问题并没有解决,题目中还有限制条件,不能只包含最多的那个字母,另一个字母至少出现一次,所以需要做一些变化,一个很直观的变化就是,用一个flag表示有没有出现,如果出现了,那就是正常的MaxSubarraySum,如果没有,那最终的结果需要-1。
这样的解法如下:

  1. class Solution:
  2. def largestVariance(self, s: str) -> int:
  3. st = set(s)
  4. res = 0
  5. for x in st:
  6. for y in st:
  7. if x != y:
  8. hasY = False
  9. acc = 0
  10. for c in s:
  11. if c != x and c != y:
  12. continue
  13. if c == x:
  14. acc += 1
  15. if hasY:
  16. res = max(res, acc)
  17. else:
  18. res = max(res, acc - 1)
  19. elif c == y:
  20. if acc > 0:
  21. hasY = True
  22. acc -= 1
  23. else:
  24. hasY = False
  25. acc = 0
  26. return res

还有一个相反的策略,不够直观,但也挺巧妙的,维护左侧累计的最小值,最大值就是 当前累计-左侧最小累计。
里边的一个小trick是,默认上一个左侧累计是0,左侧最小累计初始是inf,只有在遇到y时才更新左侧最小累计,完美解决了只要需要一个y的问题。

  1. class Solution:
  2. def largestVariance(self, s: str) -> int:
  3. st = set(s)
  4. res = 0
  5. for x in st:
  6. for y in st:
  7. if x != y:
  8. acc = 0
  9. prevDeletedPart = 0
  10. deletedPart = 10**5
  11. for c in s:
  12. if c != x and c != y:
  13. continue
  14. if c == x:
  15. acc += 1
  16. elif c == y:
  17. deletedPart = min(deletedPart, prevDeletedPart)
  18. acc -= 1
  19. prevDeletedPart = acc
  20. res = max(res, acc - deletedPart)
  21. return res