您的位置:首页 > 其它

BZOJ1396:识别子串(后缀自动机+单调队列)

2018-03-14 20:13 344 查看
题面

题意:给出一个串串,对于每个位置

求包含每个位置的,最短的,只出现一次的子串的长度

由区间的套路,只要求出以每个位置为L,最小的R,设为f[L]f[L]

显然ff单调不降

在后缀自动机上,考虑Right
4000
集大小为1的状态T

发现f[1]到f[dep[T]−Min(T)+1]f[1]到f[dep[T]−Min(T)+1]可用dep[T]更新

对于每个位置i,考虑以它为右端点

找到第一个f[h]<if[h]<i的h,i-h+1可更新ans[i]

若不以其为右端点,则j∈[h+1,i]j∈[h+1,i]的f[j]−j+1f[j]−j+1可更新ans[i]

单调队列维护即可

zenzen不用线段树哦

#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>

using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))

typedef long long LL;

const int N=400200,oo=1e9+7;

int n;
int pre
,dep
,r
,son
[26],cnt=1,last=1;
int head
,nex
,to
,cur;
int len
,ans
,q
;
char s
;

void add(int u,int v)
{
to[++cur]=v;
nex[cur]=head[u];
head[u]=cur;
}

void dfs(int x)
{
for(int h=head[x];h;h=nex[h])
dfs(to[h]),r[x]+=r[to[h]];
}

void Insert(int x)
{
dep[++cnt]=dep[last]+1;
int np=cnt,p=last;
last=cnt;
r[np]=1;
for(;!son[p][x];p=pre[p])
son[p][x]=np;
if(!p)
pre[np]=1;
else
{
int q=son[p][x];
if(dep[q]==dep[p]+1)
pre[np]=q;
else
{
dep[++cnt]=dep[p]+1;
int nq=cnt;
pre[nq]=pre[q];
pre[q]=pre[np]=nq;
mmcp(son[nq],son[q]);
for(;son[p][x]==q;p=pre[p])
son[p][x]=nq;
}
}
}

int main()
{
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1;i<=n;i++)
Insert(s[i]-'a');

for(int i=2;i<=cnt;i++)
add(pre[i],i);

dfs(1);
for(int i=1;i<=n;i++)
ans[i]=len[i]=oo;

for(int i=2;i<=cnt;i++)
if(r[i]==1)
len[dep[i]-dep[pre[i]]]=min(len[dep[i]-dep[pre[i]]],dep[i]);

for(int i=n-1;i>=1;i--)
len[i]=min(len[i],len[i+1]);

int now
aa04
=n,hh=1,tt=0;
for(int i=n;i>=1;i--)
{
while(now>=1&&len[now]>i)
{
while(hh<=tt&&len[q[tt]]-q[tt]>=len[now]-now)
tt--;
if(len[now]!=oo&&now<=i)
q[++tt]=now;
now--;
}
if(now)
ans[i]=min(ans[i],i-now+1);
if(hh<=tt)
ans[i]=min(ans[i],len[q[hh]]-q[hh]+1);
if(q[hh]==i)
hh++;
}

for(int i=1;i<=n;i++)
printf("%d\n",ans[i]);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: