bzoj 1396: 识别子串
2018-03-19 20:54
316 查看
题意:
给出一个字符串,对于每一个位置,求出最短的包含它的只在串中出现一次的子串长度。题解:
建出sam,然后处理right集合为1的串。然后线段树维护两个东西,一个以它为左端点,最靠左的合法右端点。
一个是直接包含的答案。
code:
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> using namespace std; const int inf=1<<28; char s[100010]; int n; struct SAM{ int a[26],max,par; }sam[200010];int tot,tail,root,ri[200010]; struct node{ int y,next; }a[200010];int len=0,last[200010]; struct trnode{ int lc,rc,c1,c2; }tr[200010];int trtot=0; void ins(int x,int y) { a[++len].y=y; a[len].next=last[x];last[x]=len; } void addsam(int c,int len) { int np=++tot,p=tail; sam[np].max=len;ri[np]=len; for(;p&&!sam[p].a[c];p=sam[p].par) sam[p].a[c]=np; tail=np; if(!p) sam[np].par=root; else { int q=sam[p].a[c]; if(sam[q].max==sam[p].max+1) sam[np].par=q; else { int nq=++tot;sam[nq]=sam[q]; sam[nq].max=sam[p].max+1; sam[np].par=sam[q].par=nq; for(;p&&sam[p].a[c]==q;p=sam[p].par) sam[p].a[c]=nq; } } } int ans[100010]; void change(int x,int l,int r,int fl,int fr,int c,int op) { if(l==fl&&r==fr) { if(op==1) tr[x].c1=min(tr[x].c1,c); else tr[x].c2=min(tr[x].c2,c); return; } int mid=(l+r)/2; if(fr<=mid) change(tr[x].lc,l,mid,fl,fr,c,op); else if(fl>mid) change(tr[x].rc,mid+1,r,fl,fr,c,op); else change(tr[x].lc,l,mid,fl,mid,c,op),change(tr[x].rc,mid+1,r,mid+1,fr,c,op); } void dfs(int x) { for(int i=last[x];i;i=a[i].next) { int y=a[i].y;dfs(y); if(!ri[x]) ri[x]=ri[y]; else ri[x]=(ri[x]==ri[y])?ri[y]:-1; } if(ri[x]!=-1) { int l=ri[x]-sam[x].max+1,r=ri[x],k=r-sam[sam[x].par].max; change(1,1,n,k,r,(r-k+1),1);change(1,1,n,l,k,r,2); } } int bt(int l,int r) { int x=++trtot;tr[x].c1=tr[x].c2=inf; if(l!=r) { int mid=(l+r)/2; tr[x].lc=bt(l,mid); tr[x].rc=bt(mid+1,r); } return x; } void findans(int x,int l,int r) { if(l==r) { ans[l]=min(tr[x].c1,tr[x].c2-l+1); return; } int lc=tr[x].lc,rc=tr[x].rc,mid=(l+r)/2; tr[lc].c1=min(tr[lc].c1,tr[x].c1); tr[rc].c1=min(tr[rc].c1,tr[x].c1); tr[lc].c2=min(tr[lc].c2,tr[x].c2); tr[rc].c2=min(tr[rc].c2,tr[x].c2); findans(lc,l,mid);findans(rc,mid+1,r); } int main() { root=tot=tail=1; scanf("%s",s+1); n=strlen(s+1); for(int i=1;i<=n;i++) addsam(s[i]-'a',i); for(int i=2;i<=tot;i++) ins(sam[i].par,i); bt(1,n);dfs(1); findans(1,1,n); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); }
相关文章推荐
- [BZOJ1396]识别子串-后缀树
- bzoj 1396: 识别子串 && bzoj 2865: 字符串识别【后缀数组+线段树】
- bzoj1396 识别子串
- 【BZOJ1396】识别子串
- BZOJ 1396:识别子串 SA+树状数组+单调队列
- bzoj 1396: 识别子串 (后缀自动机+线段树)
- 【bzoj1396】 识别子串
- ●BZOJ 1396 识别子串
- [后缀自动机 线段树] BZOJ 1396 识别子串 & BZOJ 2865 字符串识别
- bzoj1396: 识别子串
- BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
- BZOJ 1396&&2865 识别子串[后缀自动机 线段树]
- [BZOJ1396]识别子串 后缀自动机+线段树
- BZOJ1396:识别子串(后缀自动机+单调队列)
- 【BZOJ-1396&2865】识别子串&字符串识别 后缀自动机/后缀树组 + 线段树
- BZOJ.1396.识别子串(后缀自动机/后缀数组 线段树)
- bzoj 1396 识别子串 后缀树+线段树
- bzoj1396 识别子串【解法一】
- [BZOJ1396]识别子串(后缀自动机+线段树)
- [BZOJ 1396] 识别子串