[BZOJ1396]识别子串 后缀自动机+线段树

1396: 识别子串

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 451  Solved: 290
[Submit][Status][Discuss]

Description

Input

一行,一个由小写字母组成的字符串S,长度不超过10^5

Output

L行,每行一个整数,第i行的数据表示关于S的第i个元素的最短识别子串有多长.

Sample Input

agoodcookcooksgoodfood

Sample Output

1
2
3
3
2
2
3
3
2
2
3
3
2
1
2
3
3
2
1
2
3
4

HINT

 

Source

 

首先我们发现要找子串,想到用后缀自动机。

显然只出现一次的子串为right=1的节点(即结束节点只有一个)。对于这些节点,我们考虑对答案的贡献。

对于一个节点显然endpos-maxlen+1到endpos-minlen+1的子串是唯一的贡献为endpos-i+1。

对于endpos-minlen+1到endpos的节点,子串不唯一,我们需要将他向前延伸到endpos-minlen+1的点,贡献为minlen。

维护两棵线段树即可。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 #include<cmath>
 6 #include<algorithm>
 7 #define maxn 800005
 8 using namespace std;
 9 struct data {
10     int son[maxn][30],link[maxn],step[maxn],cnt,last;
11     int v[maxn],pos[maxn],size[maxn],endpos[maxn];
12     data() {last=cnt=1;}
13     void extend(int c,int x) {
14         int p=last,np=last=++cnt;
15         step[np]=step[p]+1;size[np]=1;endpos[np]=x;
16         while(p&&!son[p][c]) son[p][c]=np,p=link[p];
17         if(!p) link[np]=1;
18         else {
19             int q=son[p][c];
20             if(step[q]==step[p]+1) link[np]=q;
21             else {
22                 int nq=++cnt;
23                 memcpy(son[nq],son[q],sizeof(son[q]));
24                 link[nq]=link[q];
25                 link[q]=link[np]=nq;
26                 step[nq]=step[p]+1;
27                 while(son[p][c]==q&&p) son[p][c]=nq,p=link[p];
28             }
29         }
30     }
31     void pre() {
32         for(int i=1;i<=cnt;i++) v[step[i]]++;
33         for(int i=1;i<=cnt;i++) v[i]+=v[i-1];
34         for(int i=cnt;i;i--) pos[v[step[i]]--]=i;
35         for(int i=cnt;i;i--) {
36             int x=pos[i];
37             size[link[x]]+=size[x];
38             endpos[link[x]]=max(endpos[link[x]],endpos[x]);
39         } 
40     }
41     struct tmp{
42         int minx[maxn],tag[maxn];
43         tmp(){memset(minx,97,sizeof(minx));for(int i=1;i<maxn;i++) tag[i]=2147483647;}
44         void pushup(int o) {
45             int ls=o<<1,rs=ls+1;
46             minx[o]=min(minx[ls],minx[rs]);
47         }
48         void pushdown(int o) {
49             int ls=o<<1,rs=ls+1;
50             minx[ls]=min(tag[o],minx[ls]);minx[rs]=min(minx[rs],tag[o]);
51             tag[ls]=min(tag[ls],tag[o]);tag[rs]=min(tag[rs],tag[o]);
52             tag[o]=2147483647;
53         }
54         void update(int l,int r,int o,int L,int R,int val) {
55             pushdown(o);
56             if(L<=l&&R>=r) {tag[o]=min(tag[o],val);minx[o]=min(minx[o],val);return;}
57             int mid=l+r>>1,ls=o<<1,rs=ls+1;
58             if(L<=mid)update(l,mid,ls,L,R,val);
59             if(R>mid) update(mid+1,r,rs,L,R,val);
60             pushup(o);
61         }
62         int query(int l,int r,int o,int x) {
63             pushdown(o);
64             if(l==r) return minx[o];
65             int mid=l+r>>1,ls=o<<1,rs=ls+1;
66             if(x<=mid) return query(l,mid,ls,x);
67             if(x>mid) return query(mid+1,r,rs,x);
68         }
69     }t1,t2;
70     void work(int x) {
71         for(int i=1;i<=cnt;i++) {
72             if(size[i]!=1) continue;
73             int maxlen=step[i],minlen=step[link[i]]+1;
74             t1.update(1,x,1,endpos[i]-maxlen+1,endpos[i]-minlen+1,endpos[i]+1);
75             t2.update(1,x,1,endpos[i]-minlen+1,endpos[i],minlen);
76         }
77         for(int i=1;i<=x;i++) {
78             int a1=t1.query(1,x,1,i)-i,a2=t2.query(1,x,1,i);
79             printf("%d\n",min(a1,a2));
80         }
81     }
82 }sam;
83 char ch[maxn];
84 int main() {
85     scanf("%s",ch+1);
86     int len=strlen(ch+1);
87     for(int i=1;i<=len;i++) sam.extend(ch[i]-'a',i);
88     sam.pre();
89     sam.work(len);
90 } 
View Code

 

转载于:https://www.cnblogs.com/wls001/p/8253481.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值