[BZOJ4516][Sdoi2016]生成魔咒(后缀数组+链表||后缀自动机)
2016-12-28 18:38
399 查看
题目描述
传送门题解
题意实际上是求对于每一个前缀本质不同的子串个数那么可以转化为对于每一个前缀只求包含最后一个点的和前面不重复的子串个数,然后将答案累加
把串反过来建后缀数组
然后实际上就是对于每一个后缀求与其它后缀不重复的前缀个数,也即是后缀长度减去height值
但是需要注意的一点是要排除在其后面的后缀的干扰
那么可以倒序求解,求解之后将这个后缀删除,height不升,可以用链表维护一下
最后再倒序累加答案就可以了
这道题后缀自动机的做法非常裸
每加入一个点,需要统计的是生成了多少个不同的以这个点为结尾并且和前面不相同的字串,因为不会有两个串跑到相同的一个点,所以累加的个数应该为当前点合法的区间长度,也就是step(i)-step(pre(i))
代码
sa#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> using namespace std; #define LL long long #define N 100005 int n,m,cnt; int a ,b ,s ; int *x,*y,X ,Y ,c ,sa ,rank ,height ; int nxt ,pre ; LL ans ; int find(int x) { int l=1,r=cnt,mid,ans=0; while (l<=r) { mid=(l+r)>>1; if (b[mid]>=x) ans=mid,r=mid-1; else l=mid+1; } return ans; } void build_sa() { m=n; x=X,y=Y; for (int i=0;i<m;++i) c[i]=0; for (int i=0;i<n;++i) ++c[x[i]=s[i]]; for (int i=1;i<m;++i) c[i]+=c[i-1]; for (int i=n-1;i>=0;--i) sa[--c[x[i]]]=i; for (int k=1;k<=n;k<<=1) { int p=0; for (int i=n-k;i<n;++i) y[p++]=i; for (int i=0;i<n;++i) if (sa[i]>=k) y[p++]=sa[i]-k; for (int i=0;i<m;++i) c[i]=0; for (int i=0;i<n;++i) ++c[x[y[i]]]; for (int i=1;i<m;++i) c[i]+=c[i-1]; for (int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1,x[sa[0]]=0; for (int i=1;i<n;++i) x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&(sa[i-1]+k<n?y[sa[i-1]+k]:-1)==(sa[i]+k<n?y[sa[i]+k]:-1)?p-1:p++; if (p>n) break; m=p; } } void build_height() { for (int i=0;i<n;++i) rank[sa[i]]=i; int k=0;height[0]=0; for (int i=0;i<n;++i) { if (!rank[i]) continue; if (k) --k; int j=sa[rank[i]-1]; while (i+k<n&&j+k<n&&s[i+k]==s[j+k]) ++k; height[rank[i]]=k; } } int main() { scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",&b[i]),a[i-1]=b[i]; sort(b+1,b+n+1); cnt=unique(b+1,b+n+1)-b-1; for (int i=0;i<n;++i) a[i]=find(a[i])-1; for (int i=0;i<n;++i) s[i]=a[n-i-1]; build_sa(); build_height(); for (int i=0;i<n-1;++i) nxt[i]=i+1; for (int i=1;i<n;++i) pre[i]=i-1; for (int i=0;i<n;++i) { int rk=rank[i]; int now=n-i-max(height[rk],height[nxt[rk]]); ans[i]=(LL)now; height[nxt[rk]]=min(height[nxt[rk]],height[rk]); height[rk]=0; if (rk) nxt[pre[rk]]=nxt[rk]; pre[nxt[rk]]=pre[rk]; } for (int i=n-1;i>=0;--i) ans[i]+=ans[i+1]; for (int i=n-1;i>=0;--i) printf("%lld\n",ans[i]); }
sam
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<map> using namespace std; #define LL long long #define N 200005 int n,a ,last,root,sz,p,np,q,nq; int pre ,step ; map <int,int> ch ; LL ans; void extend(int x) { p=last;np=++sz;last=np; step[np]=step[p]+1; while (p&&!ch[p][x]) { ch[p][x]=np; p=pre[p]; } if (!p) pre[np]=root; else { q=ch[p][x]; if (step[q]==step[p]+1) pre[np]=q; else { nq=++sz; step[nq]=step[p]+1; ch[nq]=ch[q]; pre[nq]=pre[q]; while (p&&ch[p][x]==q) { ch[p][x]=nq; p=pre[p]; } pre[np]=pre[q]=nq; } } ans+=(LL)step[np]-step[pre[np]]; } int main() { scanf("%d",&n); for (int i=1;i<=n;++i) scanf("%d",&a[i]); root=last=++sz; for (int i=1;i<=n;++i) { extend(a[i]); printf("%lld\n",ans); } }
相关文章推荐
- caffe中网络结构参数详解
- 浅谈Java中的对象和引用
- 13.OpenGL--显示列表
- GestureDetector手势相关
- 并查集
- 将List转成树的两种方式(递归、循环)
- 缓存击穿
- Android短信发送器
- 图解MotionEvent中getRawX、getRawY与getX、getY以及View中的getScrollX、getScrollY
- 12.OpenGL--多边形偏移
- Activiti从当前任务任意回退至已审批任务
- jvm内存溢出分析
- SQL Server中 ldf 文件过大的解决方法
- ZCMU-1132
- 关于iOS的UITableView的rowheight的自动计算
- js操作table_插入+编辑+保存
- Android 安卓编程规范
- apache-php底层工作原理
- Matlab在字符串数组中找到特定字符串的位置
- 透视学的应用(六)