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; }