题解 [POI2005] SZA-Template

题目链接

充分暴露出对 \(border\) 结合 \(dp\) 理解的不足。

先来推结论,一个字符串的印章一定是其 \(border\),因为只有这样才可能兼顾首尾,但是他的 \(border\) 不一定是其印章,两个条件不能互推。

\(dp_i\) 表示前 \(i\) 个字符串的最小印章长度。现在考虑如何转移。

\(dp_i\) 的取值只有可能是 \(i\)\(dp_{fail_i}\)\(i\) 就是取自己为印章,\(dp_{fail_i}\) 表示先将其 \(border\) 想办法填出来,然后再在末尾加上剩余字符。

那么什么时候能从 \(dp_{fail_i}\) 转移过来呢,首先找到一个最大的
\(dp_j=dp_{fail_i}\),由于在 \(j\) 后面我们至多填上 \(dp_{fail_i}\) 长度的字符串,所以当 \(i-j\le dp_{fail_i}\) 时,可以转移。对于合法的 \(j\),维护一个 \(dp\) 值对应的最大下标即可。

点击查看代码
const int N=5e5+10;
string s;
int n;
int fail[N];
int maxp[N],dp[N];
int main() {
    cin>>s;
    n=s.size(); s=" "+s;
    for(int i=2;i<=n;i++) {
        int j=fail[i-1];
        while(s[j+1]!=s[i]&&j) j=fail[j];
        if(s[j+1]==s[i]) fail[i]=j+1;
    }
    for(int i=1;i<=n;i++) {
        dp[i]=i;
        maxp[dp[i]]=i;
        if(!dp[fail[i]]) continue;
        if(i-maxp[dp[fail[i]]]<=dp[fail[i]]) {
            dp[i]=dp[fail[i]];
            maxp[dp[i]]=i;
        }
    }
    cout<<dp[n];
    return 0;
}
posted @ 2023-08-08 09:21  2017BeiJiang  阅读(8)  评论(0编辑  收藏  举报