[bzoj1396]识别子串
作者:互联网
题面:
看起来有点吓人实则不然
用SA或是SAM都可以过
SAM做法:
(哪天有空再补)
SA做法:
求完SA之后,对于每一个后缀$i$
以这个后缀为起点的最短唯一子串长度应该是$max(hei[i],hei[i+1])+1$
之后用线段树覆盖对应区间就行
⑨:Baka Baka~
当然如果一个后缀为起点没有唯一子串的要特判一下
对于这样的后缀就不进行线段树覆盖
⑩:是~这样吗~
到这里时我自己突然想到不对了
假设一个后缀的最短唯一子串为$s(i,j)$,那么在$j$后面的字符怎么办?
因为这个最短唯一子串对它后面的每一位$k$也有着$k-i+1$的贡献
处理方法
ans[i]=min(ans[i],ans[i-1]+1)
本题完结
1 #include<cstdio> 2 #include<cstring> 3 const int N=100011; 4 char s[N]; 5 int n; 6 7 int max(int a,int b){return a>b?a:b;} 8 int min(int a,int b){if(!a) return b;if(!b) return a;return a<b?a:b;} 9 10 int sa[N],rank[N],hei[N]; 11 int buk[N],lst[N]; 12 bool cmp(int x,int y,int k) 13 { 14 if(x+k>n||y+k>n) return 0; 15 return rank[x]==rank[y]&&rank[x+k]==rank[y+k]; 16 } 17 void getsa() 18 { 19 int cnt=0; 20 for(int i=1;i<=n;i++) buk[s[i]]++; 21 for(int i=1;i<128;i++) if(buk[i]) lst[i]=++cnt; 22 for(int i=1;i<128;i++) buk[i]+=buk[i-1]; 23 for(int i=1;i<=n;i++) 24 { 25 rank[i]=lst[s[i]]; 26 sa[buk[s[i]]--]=i; 27 } 28 for(int k=1;cnt!=n;k<<=1) 29 { 30 for(int i=1;i<=n;i++) buk[i]=0; 31 for(int i=1;i<=n;i++) buk[rank[i]]++; 32 for(int i=1;i<=n;i++) buk[i]+=buk[i-1]; 33 for(int i=n;i;i--) 34 if(sa[i]>k) lst[sa[i]-k]=buk[rank[sa[i]-k]]--; 35 for(int i=1;i<=k;i++) 36 lst[n-i+1]=buk[rank[n-i+1]]--; 37 for(int i=1;i<=n;i++) sa[lst[i]]=i; 38 cnt=0; 39 for(int i=1;i<=n;i++) lst[sa[i]]=cmp(sa[i],sa[i-1],k)?cnt:++cnt; 40 for(int i=1;i<=n;i++) rank[i]=lst[i]; 41 } 42 } 43 void gethei() 44 { 45 for(int i=1;i<=n;i++) 46 { 47 if(rank[i]==1) continue; 48 for(int j=max(1,hei[rank[i-1]]-1);;j++) 49 { 50 if(s[i+j-1]==s[sa[rank[i]-1]+j-1]) hei[rank[i]]=j; 51 else break; 52 } 53 } 54 } 55 56 struct mokou 57 { 58 int v[N<<2]; 59 void fuckdown(int px) 60 { 61 v[px<<1]=min(v[px<<1],v[px]); 62 v[px<<1|1]=min(v[px<<1|1],v[px]); 63 return; 64 } 65 void cover(int l,int r,int c,int px,int pl,int pr) 66 { 67 if(l<=pl&&r>=pr) 68 { 69 v[px]=min(v[px],c); 70 return; 71 } 72 fuckdown(px); 73 int mid=(pl+pr)>>1; 74 if(l<=mid) cover(l,r,c,px<<1,pl,mid); 75 if(r>mid) cover(l,r,c,px<<1|1,mid+1,pr); 76 return; 77 } 78 int query(int x,int px,int pl,int pr) 79 { 80 if(pl==x&&pr==x) return v[px]; 81 fuckdown(px); 82 int mid=(pl+pr)>>1; 83 if(x<=mid) return query(x,px<<1,pl,mid); 84 else return query(x,px<<1|1,mid+1,pr); 85 } 86 }tr; 87 int ans[N]; 88 89 int main() 90 { 91 scanf("%s",s+1); 92 n=strlen(s+1); 93 getsa(); 94 gethei(); 95 for(int i=1;i<=n;i++) 96 { 97 if(sa[i]+max(hei[i],hei[i+1])>n) continue; 98 99 tr.cover(sa[i],sa[i]+max(hei[i],hei[i+1]),max(hei[i],hei[i+1])+1,1,1,n); 100 } 101 for(int i=1;i<=n;i++) 102 ans[i]=tr.query(i,1,1,n); 103 for(int i=2;i<=n;i++) ans[i]=min(ans[i],ans[i-1]+1); 104 for(int i=1;i<=n;i++) printf("%d\n",ans[i]); 105 return 0; 106 }Orz
*神的上升*
是我,水题男孩
我做后缀数组水题,好吗
不!
搜大友
*愤怒*
*激烈的对视大赛*
你够了,我无法忍受你的行为
现在你将成为我们中的议员(指做有思维含量的题)
*水题男孩上升到了新的境界*
标签:子串,return,int,rank,后缀,识别,hei,bzoj1396 来源: https://www.cnblogs.com/rikurika/p/bzoj1396.html