ACdream 1430 SETI 后缀自动机/后缀数组 不重叠子串的个数
来源:互联网 发布:如何将ubuntu安装到u盘 编辑:程序博客网 时间:2024/06/10 16:41
题目求不重叠子串的个数。
一开始看错题第一反应就是用SAM来解,求出right集,如果出现次数超过1次就统计。
不过本题要求的是不重叠的子串,因此在求right集时,顺便维护两个值,每个结点所能表示的所有原串的终点的最小值和最大值。
最后遍历所有结点统计答案。O(n)
如果用SA来做的话就是枚举所有的子串长度k,对于每个k,对heght进行分组,对每个分组,求出sa最小值和最大值,最大值减去最小值是否不小于k,如果是就把答案加一。
枚举O(n),统计O(n),总复杂度O(n^2),由于时限是10s所以没超时。
/** this code is made by cyendra* Problem: 1430* Verdict: Accepted* Submission Date: 2014-10-06 18:07:26* Time: 96MS* Memory: 14864KB*/#include <iostream>#include <cstring>#include <cstdio> using namespace std;typedef long long LL;const int INF=0x3f3f3f3f;const int maxn=30000;const int maxm=13000;/*************** SAM 真·模板***************/struct State { State *par; State *go[52]; int val; // max,当前状态能接收的串的最长长度 int mi; int mx; int right; // right集,表示当前状态可以在多少个位置上出现 void init(int _val = 0){ par = 0; val = _val; mx=-1; mi=INF; right=0; memset(go,0,sizeof(go)); } int calc(){ // 表示该状态能表示多少中不同的串 if (par==0) return 0; return val-par->val; }};State *root, *last, *cur;State nodePool[maxn];State* newState(int val = 0) { cur->init(val); return cur++;}//int total; // 不同的子串个数。void initSAM() { //total = 0; cur = nodePool; root = newState(); last = root;}void extend(int w,int pos) { State* p = last; State* np = newState(p->val + 1); np->right=1; // 设置right集 np->mi=np->mx=pos; while (p && p->go[w] == 0) { p->go[w] = np; p = p->par; } if (p == 0) { np->par = root; //total+=np->calc(); } else { State* q = p->go[w]; if (p->val + 1 == q->val) { np->par = q; //total+=np->calc(); } else { State* nq = newState(p->val + 1); memcpy(nq->go, q->go, sizeof(q->go)); //total -= q->calc(); nq->par = q->par; q->par = nq; np->par = nq; //total += q->calc()+nq->calc()+np->calc(); while (p && p->go[w] == q) { p->go[w] = nq; p = p->par; } } } last = np;} int d[maxm];State* b[maxn];void topo(){ // 求出parent树的拓扑序 int cnt=cur-nodePool; int maxVal=0; memset(d,0,sizeof(d)); for (int i=1;i<cnt;i++) maxVal=max(maxVal,nodePool[i].val),d[nodePool[i].val]++; for (int i=1;i<=maxVal;i++) d[i]+=d[i-1]; for (int i=1;i<cnt;i++) b[d[nodePool[i].val]--]=&nodePool[i]; b[0]=root;} void gaoSamInit(){ // 求出SAM的附加信息 State* p; int cnt=cur-nodePool; for (int i=cnt-1;i>0;i--){ p=b[i]; p->par->right+=p->right; p->par->mi=min(p->par->mi,p->mi); p->par->mx=max(p->par->mx,p->mx); }} char s[maxm]; int main(){ scanf("%s",s); initSAM(); for (int i=0;s[i];i++){ extend(s[i]-'a',i); } topo(); gaoSamInit(); int ans=0; State* p; int cnt=cur-nodePool; for (int i=cnt-1;i>0;i--){ p=b[i]; if (p->right>1) { int lim=p->mx-p->mi; int L=p->par->val+1; int R=p->val; if (R<=lim) ans+=p->calc(); else if (L>lim) continue; else if (lim>=L&&lim<R) ans+=lim-L+1; } } printf("%d\n",ans); return 0;}
--------
/** this code is made by cyendra* Problem: 1430* Verdict: Accepted* Submission Date: 2014-10-06 13:10:01* Time: 5740MS* Memory: 3804KB*/#include <iostream>#include <cstring>#include <cstdio> using namespace std;typedef long long LL;const int maxn=20000;const int maxm=13000;const int INF=0x3f3f3f3f;/******************************************************************** 后缀数组 Suffix Array** INIT:solver.call_fun(char* s);** CALL: solver.lcp(int i,int j); //后缀i与后缀j的最长公共前缀** SP_USE: solver.LCS(char *s1,char* s2); //最长公共字串******************************************************************/struct SuffixArray{ int r[maxn]; int sa[maxn],rank[maxn],height[maxn]; int t[maxn],t2[maxn],c[maxn],n; int m;//模板长度 void init(char* s){ n=strlen(s); for (int i=0;i<n;i++) r[i]=int(s[i]); m=300; } int cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } /** 字符要先转化为正整数 待排序的字符串放在r[]数组中,从r[0]到r[n-1],长度为n,且最大值小于m。 所有的r[i]都大于0,r[n]无意义算法中置0 函数结束后,结果放在sa[]数组中(名次从1..n),从sa[1]到sa[n]。s[0]无意义 **/ void build_sa(){ int i,k,p,*x=t,*y=t2; r[n++]=0; for (i=0;i<m;i++) c[i]=0; for (i=0;i<n;i++) c[x[i]=r[i]]++; for (i=1;i<m;i++) c[i]+=c[i-1]; for (i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for (k=1,p=1;k<n;k*=2,m=p){ for (p=0,i=n-k;i<n;i++) y[p++]=i; for (i=0;i<n;i++) if (sa[i]>=k) y[p++]=sa[i]-k; for (i=0;i<m;i++) c[i]=0; for (i=0;i<n;i++) c[x[y[i]]]++; for (i=1;i<m;i++) c[i]+=c[i-1]; for (i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; swap(x,y); p=1; x[sa[0]]=0; for (i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],k)?p-1:p++; } n--; } /** height[2..n]:height[i]保存的是lcp(sa[i],sa[i-1]) rank[0..n-1]:rank[i]保存的是原串中suffix[i]的名次 **/ void getHeight(){ int i,j,k=0; for (i=1;i<=n;i++) rank[sa[i]]=i; for (i=0;i<n;i++){ if (k) k--; j=sa[rank[i]-1]; while (r[i+k]==r[j+k]) k++; height[rank[i]]=k; } } int d[maxn][20]; //元素从1编号到n void RMQ_init(int A[],int n){ for (int i=1;i<=n;i++) d[i][0]=A[i]; for (int j=1;(1<<j)<=n;j++) for (int i=1;i+j-1<=n;i++) d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]); } int RMQ(int L,int R){ int k=0; while ((1<<(k+1))<=R-L+1) k++; return min(d[L][k],d[R-(1<<k)+1][k]); } void LCP_init(){ RMQ_init(height,n); } int lcp(int i,int j){ if (rank[i]>rank[j]) swap(i,j); return RMQ(rank[i]+1,rank[j]); } void call_fun(char* s){ init(s);//初始化后缀数组 build_sa();//构造后缀数组sa getHeight();//计算height与rank LCP_init();//初始化RMQ } int LCS(char* s1,char* s2){ int p,ans; int l=strlen(s1); p=l; s1[l]='$'; s1[l+1]='\0'; strcat(s1,s2); call_fun(s1); ans=0; for (int i=2;i<=n;i++) if ((sa[i-1]<p&&sa[i]>p)||(sa[i-1]>p&&sa[i]<p)) ans=max(ans,height[i]); return ans; } int gao(int k,int n){ int maxx=0,minn=INF,ans=0; for (int i=2;i<=n;i++){ if (height[i]<k){ if (maxx-minn>=k){ ans++; } maxx=0,minn=INF; } else{ maxx=max(maxx,max(sa[i-1],sa[i])); minn=min(minn,min(sa[i-1],sa[i])); } } if (maxx-minn>=k) ans++; return ans; }}solver; char s[maxn];int main(){ //freopen("seti.in","r",stdin); //freopen("seti.out","w",stdout); scanf("%s",s); //gets(s); int len=strlen(s); solver.init(s); solver.build_sa(); solver.getHeight(); int ans=0; for (int i=1;i<=len/2;i++){ ans+=solver.gao(i,len); } printf("%d\n",ans); return 0;}
0 0
- ACdream 1430 SETI 后缀自动机/后缀数组 不重叠子串的个数
- ACdream 1430 SETI 后缀自动机
- Acdream 1430 SETI(后缀数组)
- acdream OJ 1430 SETI 后缀数组
- acdream 1430 SETI 后缀数组+height分组
- hdu 3518 ( 后缀数组 至少出现两次的不重叠子串个数)
- HDU 3518 Boring counting 重复出现不重叠子串个数(后缀数组)
- 后缀数组(不相同子串的个数)spoj694
- HDU 4622 Reincarnation (区间不相同子串个数:字符串哈希 | 后缀数组 | 后缀自动机)
- 后缀数组 不重叠最长重复子串 POJ 1743
- 后缀数组之最长重复不重叠子串 PKU1743
- POJ 1743 后缀数组:求最长不重叠子串
- 【后缀数组 不重叠重复最长子串】POJ
- 后缀自动机(子串个数)spoj8222
- 后缀自动机(不同子串的个数)hdu4416
- HDU 4622 本质不同的子串个数:后缀自动机
- spoj220 每个字符串至少出现两次且不重叠的最长子串(后缀数组)
- 【编程珠玑】第十五章--字符串:用后缀数组查找最长不重叠的重复子串
- 服务器 libevent源码深度剖析十一
- LeetCode_First Missing Positive
- 服务器 libevent源码深度剖析十二
- git工作流指南:Pull Request工作流
- 深刻理解原类(metaclass)
- ACdream 1430 SETI 后缀自动机/后缀数组 不重叠子串的个数
- git工作流指南:功能分支工作流
- Powerbuilder数据窗口的edit.useellipsis属性
- git工作流指南:Forking工作流
- this练习
- 手把手教你:【史上最全】C++开发环境搭建:win732位下VS2010+Boost_1_53_0+Qt5.2+MySql搭建
- git工作流指南:Gitflow工作流
- 指针数组和数组指针
- hnu 13052 What does the fox say?