bzoj1396 识别子串
貌似是个傻×题啊.......由于贪心地想要刷双,结果被空间卡了半个小时。
这个题是问字符串S每一位的最短识别子串是多长(识别子串指包含这个字符且只出现在S中一次的子串)。
为了想这个题,我对着SAM的图YY了一天。终于,我发现,从后缀树的角度考虑是比较简单的。因为SAM是一棵逆序的后缀树,那么每一位i的前缀(除了包含在别的前缀里面)所对应的节点一定是后缀树里的叶子节点。那么我们只要找到它的父亲节点,然后它的父亲节点对应的子串填上一位字符就是一个识别子串。
然后我们就求出了每一位往前最短的识别子串的长度(比这个长度长的一定是识别子串)。然后,我们看:
其中L~R是最短识别字串
1~L-1段,可以用长度R-i+1更新,L^R段,可以用长度R-L+1更新。于是用两个线段树维护就行了。
strspot
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cmath> 5 #include<cstring> 6 #define maxn 500100 7 #define inf 1000000000 8 using namespace std; 9 struct node 10 { 11 node *ch[27],*pre; 12 int len; 13 bool v; 14 }sam[maxn*2],*rot,*now; 15 char s[maxn]; 16 int n,m,num; 17 struct segtree 18 { 19 int key[maxn*3],cov[maxn*3]; 20 void build(int x,int l,int r) 21 { 22 key[x]=cov[x]=inf; 23 if (l==r) return ; 24 int mid=(l+r)>>1; 25 build(x*2,l,mid); build(x*2+1,mid+1,r); 26 } 27 void cover(int x,int z) 28 { 29 cov[x]=min(cov[x],z); 30 key[x]=min(key[x],z); 31 } 32 void down(int x) 33 { 34 if (cov[x]==inf) return ; 35 cover(x*2,cov[x]); cover(x*2+1,cov[x]); 36 cov[x]=inf; 37 } 38 void add(int x,int l,int r,int ll,int rr,int z) 39 { 40 down(x); 41 if (ll<=l&&r<=rr) cover(x,z); 42 else 43 { 44 int mid=(l+r)>>1; 45 if (ll<=mid) add(x*2,l,mid,ll,rr,z); 46 if (rr>mid) add(x*2+1,mid+1,r,ll,rr,z); 47 key[x]=min(key[x*2],key[x*2+1]); 48 } 49 } 50 int ask(int x,int l,int r,int z) 51 { 52 down(x); 53 if (l==r) return key[x]; 54 int mid=(l+r)>>1; 55 if (z<=mid) return ask(x*2,l,mid,z); 56 else return ask(x*2+1,mid+1,r,z); 57 } 58 }S,T; 59 60 void insert(int w) 61 { 62 node *p=now,*np=&sam[++num]; 63 np->len=p->len+1; np->v=1; 64 while (p&&p->ch[w]==0) p->ch[w]=np,p=p->pre; 65 if (!p) np->pre=rot; 66 else 67 { 68 node *q=p->ch[w]; 69 if (p->len+1==q->len) np->pre=q; 70 else 71 { 72 node *nq=&sam[++num]; 73 *nq=*q; 74 nq->len=p->len+1; nq->v=0; 75 np->pre=q->pre=nq; 76 while (p&&p->ch[w]==q) p->ch[w]=nq,p=p->pre; 77 } 78 } 79 now=np; 80 } 81 82 int main() 83 { 84 scanf("%s",s); 85 n=strlen(s); 86 rot=now=&sam[num=0]; 87 for (int i=0;i<n;i++) insert(s[i]-'a'); 88 S.build(1,1,n); 89 T.build(1,1,n); 90 for (int i=1;i<=num;i++) 91 sam[i].pre->v=0; 92 for (int i=1;i<=num;i++) 93 if (sam[i].v==1) 94 { 95 node *p=&sam[i]; 96 int l=(p->len)-(p->pre->len),r=p->len; 97 S.add(1,1,n,l,r,r-l+1); 98 if (l>1) T.add(1,1,n,1,l-1,r); 99 } 100 for (int i=1;i<=n;i++) 101 printf("%d\n",min(S.ask(1,1,n,i),T.ask(1,1,n,i)-i+1)); 102 return 0; 103 }
其实这个题后缀数组也可以搞。
AC without art, no better than WA !