0118模拟赛订正

模拟赛还是要好好订正的....

先说下第三题吧,毕竟前两道题过于鬼畜....

起码这个题的题解还是我能接受的,虽然说考试时我还不会KMP...

这道题要求对每一个前缀我们都要求一个最小得覆盖长度....

那对于一个字符串S,设A是要求的答案,由于第一个字符和最后一个字符的限制,所以A一定是S的一个前缀和后缀,换句话说A一定是S中一个相等的前缀和后缀对应的字符串.

而想到这就不难想到KMP的next数组,因为next的定义就是以i结尾的后缀与前缀的最大长度,显然我们要用到这个数组,之后我们思考ans[i](答案数组)的构成.

存在一些性质,ans[i]要么等于i要么等于ans[next[i]]....

至于证明嘛...:

感觉和next数组的证明好像...

我们假设k是答案,且k不是ans[next[i]]那显然[0,k]与[k,i]是相等的,而这显然也满足next数组的定义,则k、应该是next[i]最大的与此矛盾...

之后我们就该考虑这样判定next[i]之间的区间能不能被next[i]的答案覆盖(也就是绿色的部分)...

这个我们就要记录一个答案数组last[i]表示到目前为止,答案长度为i覆盖最远到哪.

我们可以通过判定last[ans[next[i]]]与i-next[i]的关系来得到。

last[ans[next[i]]]表示的意义是next[i]的答案覆盖到的最远距离,如果他超过i-next[i],由于他已经覆盖了前面的的[0,next[i]]所以后面的[next[i],i]它一定能覆盖...

而i-next就是绿色区间的右段点,由于ans[next[i]]一定能覆盖[0,next[i]]所以本质还是判断next[i]的答案能不能覆盖绿色的区间...

至于代码就很短,不过这个题是真的不错....

#include<bits/stdc++.h>
using namespace std;
const int N=200010;
int last[N],next[N],ans[N],n;//last[j]数组的含义是里当前i最近的答案为j的覆盖到的距离. 
char str[N];
inline int read()
{
    int x=0,ff=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') ff=-1;ch=getchar();}
    while(isdigit(ch)) {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    return x*ff;
}
inline void KMP()
{
    next[1]=0;
    ans[1]=1;
    last[1]=1;
    for(int i=2,j=0;i<=n;++i)
    {
        while(j>0&&str[i]!=str[j+1]) j=next[j];
        if(str[i]==str[j+1]) ++j;
        next[i]=j;
        if(next[i]==0) ans[i]=i;
        else 
        {
            if(last[ans[next[i]]]>=i-next[i]) ans[i]=ans[next[i]];
            else ans[i]=i;
        }
        last[ans[i]]=i;
    }
}
int main()
{
    freopen("cover.in","r",stdin);
    freopen("cover.out","w",stdout);
    scanf("%s",str+1);
    n=strlen(str+1);
    KMP();
    for(int i=1;i<=n;++i) printf("%d ",ans[i]);
    return 0;
}
posted @ 2020-01-19 17:28  逆天峰  阅读(172)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//