MENU

Solution. LG3426 [POI2005]SZA-Template

Description

用一个印章印出字符串 $S$,印章每次会将所有字符印到纸上,印上去的字母无法被覆盖,求最小的印章长度。

$|S|\le5\times10^5$

Solution

若印章能印出 $S$,则必然能印出 $S$ 的最长 border,即 $nxt_i$,这启发我们 dp。

设 $f_i$ 表示能引出 $S_{0\dots i}$ 的最小印章长度,则 $f_i=i\ \mathrm{or}\ f_{nxt_i}$。

考虑什么时候能够通过 $nxt_i$ 转移,这一点题解里普遍很简略。

已知,$S_{0\dots i}$ 的 border 部分能够通过印章印出,我们希望知道当前印章能否印出中间的部分,于是我们查找是否存在 $f_j$ 使得 $f_j=f_{nxt_i},j\ge i-nxt_i$,前者保证了 $f_j$ 与 $f_{nxt_i}$ 对应了同一个印章,后者保证了 $f_j$ 覆盖了我们未知的部分。

具体实现时我们维护每个 dp 值对应的最大下标即可。

Code

namespace Main{
    const int N = 500005;
    int n, nxt[N], f[N], cnt[N];
    char s[N];
    void Main(){
        scanf("%s", s + 1);
        n = strlen(s + 1);
        for(int i = 2, j = 0; i <= n; i++){
            while(j && s[i] != s[j+1]) j = nxt[j];
            if(s[i] == s[j+1]) j++;
            nxt[i] = j;
        }
        for(int i = 1; i <= n; i++){
            f[i] = i;
            if(cnt[f[nxt[i]]] >= i - nxt[i]) f[i] = f[nxt[i]];
            cnt[f[i]] = i;
        }
        write(f[n]);
        return;
    }
}
Last Modified: October 23, 2022
Archives Tip
QR Code for this page
Tipping QR Code