1 条题解

  • 0
    @ 2026-4-19 0:28:35

    这题的题解多为远古题解,故读起来多有误解与盲区,因此写了这篇题解。

    题目简述

    给定你要印刷的文本串 SS

    你决定刻一个印章,印章每使用一次,就会将印章上的所有字母印到纸上。

    同一个位置的相同字符可以印多次。例如:用 aba 这个印章可以完成印制 ababa 的工作(中间的 a 被印了两次)。但是,因为印上去的东西不能被抹掉,在同一位置上印不同字符是不允许的。例如:用 aba 这个印章不可以完成印制 abcba 的工作。

    你希望印章的字符串长度尽可能小。

    思路

    以下是我们的约定 & 发现:

    • ss 可以tt 印刷,当且仅当可以通过若干次印印章 tt 得到一字符串 TT,使得 ssTT 的子串。

    • ss 正好tt 印刷,当且仅当可以通过若干次印印章 tt 得到一字符串 TT,使得 s=Ts = T

    • s[l,r]s[l,r] 表示 slsl+1srs_{l}s_{l+1}\dots s_{r}

    • ttssBorder\text{Border} 当且仅当 tt 既是 ss 的前缀,也是 ss 的后缀,可以发现若 ss 正好被印刷必有印章 tt 是其的 Border\text{Border}

    • ttss 的真 Border\text{Border} 当且仅当 tt 既是 ss 的前缀,也是 ss 的后缀,且 sts \ne t,同时定义 nxtinxt_i 表示 S[1,i]S[1,i] 的最长 Border\text{Border} 长度。

    • 字符串 ss 的任意真 Border\text{Border} 一定是 ss 最长真 Border\text{Border}Border\text{Border}。(有点绕,可以画下图,这里简单解释一下,其实就是 KMP 的一些思想)

    • 由上一条可以推广到对于任意的印章 tt,若 ttss 的真 Border\text{Border},且正好印刷 ss,则 tt 也可以正好印刷 ss 的最长真 Border\text{Border}。(解释

    考虑 DP,令 dpidp_i 表示将 S[1,i]S[1,i] 正好印刷所需的最小印章长度。

    可以发现,dpidp_i 只可能等于 iidpnxtidp_{nxt_i}dpi=idp_i = i 很显然,直接令印章 T=S[1,i]T = S[1,i] 即可,而 dpi=dpnxtidp_i=dp_{nxt_i} 可以用上述中的第 77 条理解。

    但不是何时都能等于 dpnxtidp_{nxt_i},又是那个例子,假设 S[1,i]S[1,i]2424,形如这样:#######__________#######

    可以发现,中间有 1010 个其他字符,如果印章长度只有 dpnxtidp_{nxt_i} 可能印刷不到,那么何时才能取 dpnxtidp_{nxt_i} 呢?

    而只要我们把中间一段覆盖到就可以取到 dpnxtidp_{nxt_i},就比如这样:

    #######__________#######
                     #######
    $$$$$$$$$$$$$$$$$ 第一种:正好与后面一段的 Border 衔接
    $$$$$$$$$$$$$$$$$$$$ 第二种:与后面一段的 Border 有交集
    

    即需存在 jj,使得 dpj=dpnxtidp_j = dp_{nxt_i}(显然 S[1,j]S[1,j]S[1,nxti]S[1,nxt_i] 使用的印章要相同吧?) 且 inxtij<ii - nxt_i \le j < i,实现时开个桶就行。

    这里略微解释下为什么 dpj=dpnxtidp_j = dp_{nxt_i}S[1,j]S[1,j]S[1,nxti]S[1,nxt_i] 使用的印章相同,因为“若 ss 正好被印刷必有印章 tt 是其的 Border\text{Border}” 由 Border\text{Border} 的定义有 ttss 的前缀,所以 S[1,nxti]S[1,nxt_i] 使用的印章为 S[1,dpnxti]S[1,dp_{nxt_i}]S[1,j]S[1,j] 使用的印章为 S[1,dpj]S[1,dp_j],又因为 dpj=dpnxtidp_j = dp_{nxt_{i}},所以它俩用的印章相同。

    具体实现见代码。

    复杂度分析

    • 时间复杂度:KMP 求 nxtnxt 数组 O(n)O(n),DP 也是 O(n)O(n),总共 O(n)O(n)

    • 空间复杂度:O(n)O(n)

    #include<bits/stdc++.h>
    
    using namespace std;
    using ll = long long;
    
    const int MAXN = 5e5 + 5;
    
    string s;
    int n, nxt[MAXN], dp[MAXN], t[MAXN];
    
    int main(){
      ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
      cin >> s;
      n = s.size(), s = "#" + s;
      for(int i = 2; i <= n; i++){
        int pos = nxt[i - 1];
        for(; pos && s[i] != s[pos + 1]; pos = nxt[pos]);
        nxt[i] = s[i] == s[pos + 1] ? pos + 1 : 0;
      }//KMP 求 nxt 数组
      dp[1] = 1, t[dp[1]] = 1;//注意初始化问题
      for(int i = 2; i <= n; i++){
        if(t[dp[nxt[i]]] >= i - nxt[i]){//存在符合要求的 j
          dp[i] = dp[nxt[i]];
        }else{
          dp[i] = i;//否则只能为 i
        }
        t[dp[i]] = i;//只取 dp[i] 相同的 i 的最大值
      }
      cout << dp[n];
      return 0;
    }
    
    • 1

    信息

    ID
    3190
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者