ssoj 2511 后缀数组
来源:互联网 发布:ssh登录权限管理源码 编辑:程序博客网 时间:2024/06/11 10:53
离线作法,先将整个字符串读入生成一棵tire。
1.在tire上一个后缀的定义就是一个节点到末尾的路径。
2.那么接下来考虑如何维护这些后缀的sa,和rank。
3.会发现一个后缀在树上是一段连续的“树枝”,不会拐弯,这个性质非常的有用。由此,我们倍增计算sa和rank时只需像倍增求lca一样往上跳2的j次方个节点就行了。
4.接下来是lcp的问题,在这里我们可以不需要求出height数组,只需要将每一次倍增计算出的rank值保存下来.
rank[i][j]表示以i开头的后缀在第j次排序中的排名,即在tire上向上跳2^j步后,以那个节点为开头的后缀的排名,如果排名相同,就是一个前缀。
#include<iostream>#include<cstdio>#include<cstdlib>#include<algorithm>#include<cstring>#include<string>#include<cmath>#include<set>#define LL long longusing namespace std;const int maxn=200063;char ch[maxn];int r[maxn],len,las,cnt,logs=18;int wa[maxn],wb[maxn],wv[maxn],wr[maxn],sa[maxn],num[maxn];int d[maxn],f[maxn][25],rank[maxn][25];LL ans[maxn];struct Node{ int x; bool operator<(const Node &rhs)const{ return rank[x][logs]<rank[rhs.x][logs]; }};set<Node> s;bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[f[a][l]]==r[f[b][l]];}void build_sa(int n,int m){ int *x=wa,*y=wb,*t; for(int i=1;i<=m;i++) wr[i]=0; for(int i=1;i<=n;i++) wr[x[i]=r[i]-96]++; for(int i=2;i<=m;i++) wr[i]+=wr[i-1]; for(int i=n;i>=1;i--) sa[wr[i]--]=i; for(int i=1;i<=n;i++) rank[i][0]=x[i]; for(int j=0,p;j<logs;j++,m=p){ for(int i=0;i<=m;i++) wr[i]=0; for(int i=1;i<=n;i++){ if(!f[i][j]) wr[wv[i]=0]++; else wr[wv[i]=x[f[i][j]]]++,f[i][j+1]=f[f[i][j]][j]; } for(int i=1;i<=m;i++) wr[i]+=wr[i-1]; for(int i=n;i>=1;i--) y[wr[wv[i]]--]=i; for(int i=1;i<=n;i++) wv[i]=x[y[i]];//用第二关键字排序后的结果 for(int i=0;i<=m;i++) wr[i]=0; for(int i=1;i<=n;i++) wr[wv[i]]++; for(int i=2;i<=m;i++) wr[i]+=wr[i-1]; for(int i=n;i>=1;i--) sa[wr[wv[i]]--]=y[i]; t=x;x=y;y=t; rank[sa[1]][j+1]=x[sa[1]]=1;p=1; for(int i=2;i<=n;i++){ rank[sa[i]][j+1]=x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p:++p; } }}int lcp(int a,int b){ int res=0; for(int i=logs;i>=0;i--){ if(rank[a][i]==rank[b][i]) res+=(1<<i),a=f[a][i],b=f[b][i]; } return res;}int main(){ scanf(" %s",ch+1); len=strlen(ch+1); las=0; for(int i=1;i<=len;i++){ if(isalpha(ch[i])){ f[++cnt][0]=las;d[cnt]=d[las]+1; las=cnt;r[las]=ch[i]; } else las=f[las][0]; num[i]=las; } build_sa(cnt,30); for(int i=1;i<=len;i++){ int now=num[i]; if(isalpha(ch[i])){ ans[now]=ans[f[now][0]]+d[now]; //cout<<ans[now]<<endl; set<Node>::iterator nxt=s.lower_bound((Node){now}),pre; if(nxt!=s.end()) ans[now]-=lcp(now,nxt->x); if(nxt!=s.begin()){ pre=nxt;pre--; ans[now]-=lcp(now,pre->x); if(nxt!=s.end()) ans[now]+=lcp(nxt->x,pre->x); } s.insert((Node){now}); } else s.erase((Node){num[i-1]}); printf("%lld\n",ans[now]); } return 0;}
0 0
- ssoj 2511 后缀数组
- 后缀树/后缀数组
- 后缀树 后缀数组
- 【后缀数组】后缀排序
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 后缀数组
- 金融类app各种数值的精确计算问题
- ios基础之调试篇
- struts拦截器
- weblogic 服务不响应java.net.SocketTimeoutException: Read timed out
- 自动变色自定义View和自定义关键字变色TextView
- ssoj 2511 后缀数组
- 深入浅出 消息队列 ActiveMQ
- 安装VMWare tools,以及解决安装后/mnt中有hgfs但没共享文件的方法
- 关于Manifest merger failed
- iPad Air/Air2/iPhone6 Plus跑分对比
- tomcat 加载一个类两次
- Android-Activity 启动模式解析
- RabbitMQ基础概念详细介绍
- IOS之sha加密、md5常规加密、md5二次加密详解及示例程序