Description
我们称一个字符串A 覆盖了一个字符串B 当且仅当对于B 中的每一个字符,都有一个包含它的和A 相同的子串。
例如,A={1,2,1}覆盖了B={1,2,1,2,1,1,2,1}。
所谓的最短覆盖子串,指的是覆盖该串的最短子串。
例如B 的最短覆盖子串为A,长度为3。
最短覆盖前缀数组指的是对于一个串的每一个前缀,它们的最短覆盖子串长度按顺序组成的数组。
例如B 的最短覆盖前缀数组为{1,2,3,2,3,6,7,3}。
现在给你一个最短覆盖前缀数组,判断是否存在某个串符合条件,如果存在则给出一组解。
对于100%的数据满足所有数据的n 之和不超过500,000 且t<=10。
Solution
并没有什么头绪~
看了题解才大概理解
对于一个前缀i的最短覆盖子串,要么是i,要么是next[i]的最短覆盖子串。
可以是next[i]的条件就是i的前next[i]个最短覆盖子串中也有一个next[i]的最短覆盖子串。
因为这样才可以将整个串覆盖。
那么我们就得到了一种check的方法:将你构造出来的串还原成最短覆盖前缀数组,判断和给出的是否等价。
如何构造呢?
我们发现如果i的最短覆盖子串为pi,那么区间[1,pi]和[i-pi+1,i]是一一对应的。
用ST表把它们合并。
最后把在同一个集合中的位置全部变成同一个数。
正确性感性理解一下吧。。。(不要鄙视蒟蒻)
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=5*1e5+5;
int f[N*19],id[N][19],pos[N*19],mi[19];
int a[N],c[N],an[N],h[N],next[N];
int n,tot,cnt,ty;
int get(int x) {
return f[x]?f[x]=get(f[x]):x;
}
void merge(int x,int y) {
x=get(x);y=get(y);
if (x>y) swap(x,y);
if (x==y) return;
f[y]=x;
}
bool check() {
memset(next,0,sizeof(next));
memset(h,0,sizeof(h));
int k=0;
fo(i,2,n) {
while (k&&c[k+1]!=c[i]) k=next[k];
next[i]=k+=c[k+1]==c[i];
}
an[1]=1;h[1]=1;
fo(i,2,n) {
if (h[an[next[i]]]>=i-next[i]) an[i]=an[next[i]];
else an[i]=i;
h[an[i]]=i;
}
fo(i,1,n) if (an[i]!=a[i]) return 0;
return 1;
}
int main() {
freopen("chuan.in","r",stdin);
freopen("chuan.out","w",stdout);
for(scanf("%d",&ty);ty;ty--) {
scanf("%d",&n);
cnt=tot=0;
memset(f,0,sizeof(f));
int lg=log(n)/log(2);
mi[0]=1;fo(i,1,lg) mi[i]=mi[i-1]*2;
fo(i,0,lg)
fo(j,1,n-mi[i]+1) {
pos[++tot]=j;
id[j][i]=tot;
}
fo(i,1,n) {
scanf("%d",&a[i]);
if (a[i]==i) continue;
int x=a[i],l1=1,l2=i-x+1;
fd(j,lg,0) if (x>=mi[j]) {
x-=mi[j];
merge(id[l1][j],id[l2][j]);
l1+=mi[j];l2+=mi[j];
}
}
fd(i,lg,1)
fo(j,1,n-mi[i]+1) {
int x=get(id[j][i]);
int l=pos[x];
merge(id[j][i-1],id[l][i-1]);
merge(id[j+mi[i-1]][i-1],id[l+mi[i-1]][i-1]);
}
fo(i,1,n) {
int x=get(id[i][0]);
if (x==id[i][0]) c[i]=++cnt;
else c[i]=c[x];
}
if (check()) {
printf("Yes\n");
fo(i,1,n) printf("%d ",c[i]);
printf("\n");
} else printf("No\n");
}
}