• 【Leetcode】664. Strange Printer(配数学证明)


    题目地址:

    https://leetcode.com/problems/strange-printer/

    给定一个长 n n n字符串 s s s,有一个刷子和一个长 n n n的白板,可以每次将任意位置开始的任意长度的段刷成同一个字符,先刷的会被后刷的覆盖。问要刷出 s s s至少要刷多少次。

    首先,可以将 s s s去个重,即如果相邻位置字符相等,可以缩成一个,因为无论是什么方案,刷任意其中一个字符,都可以延伸着刷整个相同的一段。

    f [ l ] [ r ] f[l][r] f[l][r] s [ l : r ] s[l:r] s[l:r]至少要刷多少次。那么当 l = r l=r l=r的时候, f [ l ] [ r ] = 1 f[l][r]=1 f[l][r]=1;考虑最后一次刷到 s [ l ] s[l] s[l]的那一次,假设这次刷的是 s [ l : k ] s[l:k] s[l:k],那么我们可以先将这次之前刷 s [ l : k ] s[l:k] s[l:k]这部分的操作都变为只刷 s [ k + 1 : ] s[k+1:] s[k+1:]这部分,并且将刷 s [ l : k ] s[l:k] s[l:k]的操作提前为第一次操作,这样显然所得方案的总操作次数不差于原来的方案。所以我们只需要考虑第一次操作就刷了 s [ l ] s[l] s[l]的所有方案。

    枚举 k k k,当 k = l k=l k=l时, f [ l ] [ r ] = 1 + f [ l + 1 ] [ r ] f[l][r]=1+f[l+1][r] f[l][r]=1+f[l+1][r];当 k > l k>l k>l时,如果 s [ l ] ≠ s [ k ] s[l]\ne s[k] s[l]=s[k],那么所有第一步刷 s [ i : k ] s[i:k] s[i:k]的方案都可以转化为第一步刷 s [ i : k − 1 ] s[i:k-1] s[i:k1]的方案(因为 s [ l ] ≠ s [ k ] s[l]\ne s[k] s[l]=s[k] s [ k ] s[k] s[k]即使第一步刷了,之后也会覆盖,所以可以第一步刷 s [ l : k − 1 ] s[l:k-1] s[l:k1],操作数也一样),同理,如果 s [ l ] ≠ s [ k − 1 ] s[l]\ne s[k-1] s[l]=s[k1],则可以转为先刷 s [ l : k − 2 ] s[l:k-2] s[l:k2]以此类推。所以所有的方案都可以转化为 s [ l ] = s [ k ] s[l]=s[k] s[l]=s[k]的方案,从而我们只需要枚举这种类型的方案即可。对于 s [ l ] = s [ k ] s[l]=s[k] s[l]=s[k]且第一步刷 s [ l : k ] s[l:k] s[l:k]的方案来说,这种方案都可以对应 f [ l ] [ k − 1 ] f[l][k-1] f[l][k1]的方案(第一步将 s [ l : k − 1 ] s[l:k-1] s[l:k1]刷成 s [ l ] s[l] s[l],之后的所有操作步骤忽略 s [ k ] s[k] s[k]即可),并且对于 f [ l ] [ k − 1 ] f[l][k-1] f[l][k1]的最优方案,它也可以转化为刷 s [ l : k ] s[l:k] s[l:k]的方案(第一步变为刷 s [ l : k ] s[l:k] s[l:k] s [ l ] s[l] s[l]即可,其余操作原样照做),所以此时 f [ l ] [ k ] = f [ l ] [ k − 1 ] f[l][k]=f[l][k-1] f[l][k]=f[l][k1]。后面的部分最少操作次数即为 f [ k + 1 ] [ r ] f[k+1][r] f[k+1][r],所以此时 f [ l ] [ r ] = min ⁡ l < k ≤ r { f [ l ] [ k − 1 ] + f [ k + 1 ] [ r ] } f[l][r]=\min_{lf[l][r]=l<krmin{f[l][k1]+f[k+1][r]}当然 k = r k=r k=r的时候取 f [ k + 1 ] [ r ] = 0 f[k+1][r]=0 f[k+1][r]=0。代码如下:

    class Solution {
     public:
      int strangePrinter(string s) {
        int n = 0;
        // 去重
        for (int i = 0; i < s.size(); i++)
          if (!n || s[i] != s[n - 1]) s[n++] = s[i];
        s.resize(n);
    
        int f[n][n];
        for (int len = 1; len <= n; len++)
          for (int l = 0; l + len - 1 < n; l++) {
            int r = l + len - 1;
            if (len == 1) f[l][r] = 1;
            else {
              f[l][r] = 1 + f[l + 1][r];
              for (int i = l + 1; i <= r; i++)
                if (s[i] == s[l])
                  f[l][r] = min(f[l][r], f[l][i - 1] + (i + 1 <= r ? f[i + 1][r] : 0));
            }
          }
    
        return f[0][n - 1];
      }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    时间复杂度 O ( n 3 ) O(n^3) O(n3),空间 O ( n 2 ) O(n^2) O(n2)

  • 相关阅读:
    基于单片机的红外遥控解码程序设计与实现
    GDB调试运行程序Issta
    vue-cropper在ie11下选择本地图片后,无显示、拒绝访问的问题
    图文并茂quasar2.6+vue3+ts+vite创建项目并引入mockjs,mockjs 拦截ajax请求的原理是什么,quasar为什么要使用boot?
    Java Gradle
    从零开始写 Docker(十六)---容器网络实现(上):为容器插上”网线”
    Qt5.15:MinGW64位编译Oracle 19c数据库驱动及代码测试 - 安装时没有选Sources处理办法
    HTTP基础
    分布式id解决方案
    LoongArch 指令集 流水线设计
  • 原文地址:https://blog.csdn.net/qq_46105170/article/details/128060459