后缀自动机
今天重新看了一下后缀自动机的,感受颇深。
先贴个模板:
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=2*10000; int N; char s[maxN+1000]; struct SAM { struct node { int ch[26],fail,maxlen,minlen,size; /* maxlen:能接受的最长字符串长度 minlen:能接受的最短字符串长度,为fail的maxlen+1 maxlen-minlen+1:能接受的不同字符串种数 size:表示这个状态出现了几次,状态能够表示的所有字符串均出现过size次 fail-tree恰好是字符串逆序的后缀树 ch构成一个有向无环图 */ inline void clear(){mmst(ch,0);fail=maxlen=minlen=size=0;} }; node sn[maxN*2+1000];//空间要乘2 int idx,last; inline void clear(){idx=last=1;sn[idx].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int c) { int end=newnode(); int p=last; last=end;//注意这句话 sn[end].maxlen=sn[p].maxlen+1; for(;p && !sn[p].ch[c];p=sn[p].fail)sn[p].ch[c]=end;//这里生成了新的子串 if(!p)sn[end].fail=1;//注意root是1 else { int q=sn[p].ch[c]; if(sn[p].maxlen+1==sn[q].maxlen)sn[end].fail=q; else { int nq=newnode(); sn[nq]=sn[q]; sn[nq].maxlen=sn[p].maxlen+1; sn[q].fail=sn[end].fail=nq; for(;p && sn[p].ch[c]==q;p=sn[p].fail)sn[p].ch[c]=nq; } } sn[end].minlen=sn[sn[end].fail].maxlen+1; /* for(int p=end;p!=1;p=sn[p].fail)sn[p].size++; 这样写为暴力修改size,遇到连续个相同字符的数据就会超时 可以用LCT优化,具体见bzoj2555 */ } int cnt[maxN+100],b[2*maxN+100]; inline void insert(char *s,int N) { int i,p; clear(); re(i,1,N)add(s[i]-'a'); re(i,1,N)cnt[i]=0; re(i,1,idx)cnt[sn[i].maxlen]++; re(i,1,N)cnt[i]+=cnt[i-1]; re(i,1,idx)b[cnt[sn[i].maxlen]--]=i; p=1;re(i,1,N){p=sn[p].ch[s[i]-'a'];sn[p].size++;} red(i,N,1){p=b[i];if(sn[p].fail!=1)sn[sn[p].fail].size+=sn[p].size;} } };
poj1509
【题意】
求一个字符串的最小表示。
【解析】
虽然这道题有更简单的做法。。。。。。
先将这个字符串复制一遍,然后做SAM。然后从根结点开始,不断找最小的儿子走,直到长度为N为止。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=2*10000; int N; char s[maxN+1000]; struct SAM { struct node { int ch[26],fail,len; inline void clear(){mmst(ch,0);fail=len=0;} }; node sn[maxN*2+1000];//空间要乘2 int idx,last; inline void clear(){idx=last=1;sn[idx].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int c) { int end=newnode(); int p=last; last=end;//注意这句话 sn[end].len=sn[p].len+1; for(;p && !sn[p].ch[c];p=sn[p].fail)sn[p].ch[c]=end; if(!p){sn[end].fail=1;return;}//注意root是1 int q=sn[p].ch[c]; if(sn[p].len+1==sn[q].len){sn[end].fail=q;return;} int nq=newnode(); sn[nq]=sn[q]; sn[nq].len=sn[p].len+1; sn[q].fail=sn[end].fail=nq; for(;p && sn[p].ch[c]==q;p=sn[p].fail)sn[p].ch[c]=nq; } inline void insert(char *s,int N) { int i; clear(); re(i,1,N)add(s[i]-'a'); } }; SAM sam; int main() { freopen("poj1509.in","r",stdin); freopen("poj1509.out","w",stdout); int i,j; for(int Case=gint();Case;Case--) { scanf("%s\n",s+1); N=strlen(s+1); re(i,1,N)s[N+i]=s[i]; sam.insert(s,2*N); int p=1; re(i,1,N)re(j,0,26-1) if(sam.sn[p].ch[j]){p=sam.sn[p].ch[j];break;} printf("%d\n",sam.sn[p].len-N+1); } return 0; }
spoj1811
【题意】
求两个字符串的最长公共子串。
【解析】
对第一个字符串跑SAM。然后第二个字符串找SAM里跑,如果没有对应的儿子就找fail,类似于KMP,注意对len的理解。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=250000; struct SAM { struct node { int ch[26],fail,maxlen; inline void clear(){mmst(ch,0);fail=maxlen=0;} }; node sn[2*maxN+1000]; int idx,last; inline void clear(){sn[idx=last=1].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int c) { int end=newnode(); int p=last; last=end; sn[end].maxlen=sn[p].maxlen+1; for(;p && !sn[p].ch[c];p=sn[p].fail)sn[p].ch[c]=end; if(!p){sn[end].fail=1;return;} int q=sn[p].ch[c]; if(sn[p].maxlen+1==sn[q].maxlen){sn[end].fail=q;return;} int nq=newnode(); sn[nq]=sn[q]; sn[nq].maxlen=sn[p].maxlen+1; sn[q].fail=sn[end].fail=nq; for(;p && sn[p].ch[c]==q;p=sn[p].fail)sn[p].ch[c]=nq; } inline void insert(char *s,int N) { int i; clear(); re(i,1,N)add(s[i]-'a'); } }; int lena,lenb; char a[maxN+100],b[maxN+100]; SAM sam; int ans; int main() { freopen("spoj1811.in","r",stdin); freopen("spoj1811.out","w",stdout); int i; scanf("%s\n",a+1);lena=strlen(a+1); scanf("%s\n",b+1);lenb=strlen(b+1); sam.insert(a,lena); ans=0; int clen=0,p=1; re(i,1,lenb) { int c=b[i]-'a'; if(sam.sn[p].ch[c]) { p=sam.sn[p].ch[c]; clen++; } else { while(p!=1 && !sam.sn[p].ch[c])p=sam.sn[p].fail; if(!sam.sn[p].ch[c]) clen=0; else { clen=sam.sn[p].maxlen+1; p=sam.sn[p].ch[c]; } } upmax(ans,clen); } cout<<ans<<endl; }
spoj1812
【题意】
求多个字符串的最长公共子串
【解析】
首先对第一个串做SAM。我们想知道对于SAM的每个节点,和剩下的每个字符串的最长公共子串是多少,然后所有的子串取一个最小值。
上题类似的做法,不过要更新fail树的祖先的答案,可以最后在延迟更新。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxT=10; const int maxN=100000; struct SAM { struct Tnode { int ch[26],fail,l,nl,ml; inline void clear(){mmst(ch,0);fail=l=nl=ml=0;} }sn[2*maxN+1000]; int idx,last; inline void clear(){sn[idx=last=1].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int z) { int end=newnode(); int p=last; last=end; sn[end].l=sn[p].l+1; for(;p && !sn[p].ch[z];p=sn[p].fail)sn[p].ch[z]=end; if(!p)sn[end].fail=1; else { int q=sn[p].ch[z]; if(sn[p].l+1==sn[q].l) sn[end].fail=q; else { int nq=newnode(); sn[nq]=sn[q]; sn[q].fail=sn[end].fail=nq; sn[nq].l=sn[p].l+1; for(;p && sn[p].ch[z]==q;p=sn[p].fail)sn[p].ch[z]=nq; } } } int cnt[maxN+100],b[2*maxN+100]; inline void insert(char *s) { int i,N=strlen(s+1); clear(); re(i,1,N)add(s[i]-'a'); re(i,1,N)cnt[i]=0; re(i,1,idx)cnt[sn[i].l]++; re(i,1,N)cnt[i]+=cnt[i-1]; re(i,1,idx)b[cnt[sn[i].l]--]=i; } }; int T; char s[maxT+3][maxN+1000]; SAM sam; int main() { freopen("spoj1812.in","r",stdin); freopen("spoj1812.out","w",stdout); int i,j; while(scanf("%s\n",s[++T]+1)!=EOF);T--; sam.insert(s[1]); re(i,2,sam.idx)sam.sn[i].ml=sam.sn[i].l; re(i,2,T) { int N=strlen(s[i]+1),p=1,len=0; re(j,1,N) { int z=s[i][j]-'a'; while(p!=1 && !sam.sn[p].ch[z]){p=sam.sn[p].fail;len=sam.sn[p].l;} if(sam.sn[p].ch[z]){len++;p=sam.sn[p].ch[z];} upmax(sam.sn[p].nl,len); } red(j,sam.idx,2) { int p=sam.b[j]; upmin(sam.sn[p].ml,sam.sn[p].nl); if(sam.sn[p].fail!=1) upmax(sam.sn[sam.sn[p].fail].nl,sam.sn[p].nl); sam.sn[p].nl=0; } } int ans=0; re(i,1,sam.idx)upmax(ans,sam.sn[i].ml); cout<<ans<<endl; return 0; }
hdu4641
【题意】
给定一个原始的字符串,有m次操作,每次操作可以向该字符串末尾添加一个字符或者询问在字符串中出现了至少K次的子串一共有多少个。
【解析】
就是在模板中求size值大于等于K的个数。这里有个要点,如果大于K就不必再询问祖先了。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=50000+200000; const int maxM=200000; const int maxK=50000; int N,K,M; LL ans; struct SAM { struct Tnode { int ch[26],fail,l,right; inline void clear(){mmst(ch,0);fail=l=right=0;} }node[2*maxN+1000]; int idx,last; inline void clear(){node[idx=last=1].clear();} inline int newnode(){node[++idx].clear();return idx;} inline void add(int z) { int end=newnode(); int p=last; last=end; node[end].l=node[p].l+1; for(;p && !node[p].ch[z];p=node[p].fail)node[p].ch[z]=end; if(!p)node[end].fail=1; else { int q=node[p].ch[z]; if(node[p].l+1==node[q].l)node[end].fail=q; else { int nq=newnode(); node[nq]=node[q]; node[q].fail=node[end].fail=nq; node[nq].l=node[p].l+1; for(;p && node[p].ch[z]==q;p=node[p].fail)node[p].ch[z]=nq; } } for(p=end;p!=1;p=node[p].fail) { node[p].right++; if(node[p].right>K)break; if(node[p].right==K) { int ge=node[p].l-node[node[p].fail].l; ans+=LL(ge); } } } }; SAM sam; int main() { freopen("hdu4641.in","r",stdin); freopen("hdu4641.out","w",stdout); int i; while(scanf("%d%d%d\n",&N,&M,&K)!=EOF) { sam.clear(); ans=0; re(i,1,N) { char z=getchar(); sam.add(z-'a'); } while(M--) { int flag=gint(); if(flag==2) printf("%I64d\n",ans); else { char z=getchar(); sam.add(z-'a'); } } } return 0; }
spoj7258
【题意】
求去重后的第K大子串。
【解析】
SAM是一个有向无环图。
考虑诸位确定答案。
假设我们知道下步分别取a,b,c,......,z的方案数,我们就可以从a到z枚举,如果方案数小于K那么,K减去方案数,否则下一位就选这个字母。
方案数可以用拓扑序确定。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=90000; struct SAM { struct Tnode { int ch[26],fail,l; LL cnt; inline void clear(){mmst(ch,0);fail=l=0;cnt=0;} }sn[2*maxN+100]; int idx,last; inline void clear(){sn[idx=last=1].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int z) { int end=newnode(); int p=last; last=end; sn[end].l=sn[p].l+1; for(;p && !sn[p].ch[z];p=sn[p].fail)sn[p].ch[z]=end; if(!p)sn[end].fail=1; else { int q=sn[p].ch[z]; if(sn[p].l+1==sn[q].l)sn[end].fail=q; else { int nq=newnode(); sn[nq]=sn[q]; sn[nq].l=sn[p].l+1; sn[q].fail=sn[end].fail=nq; for(;p && sn[p].ch[z]==q;p=sn[p].fail)sn[p].ch[z]=nq; } } } inline void insert(char *s) { int i,N=strlen(s+1); clear(); re(i,1,N)add(s[i]-'a'); } }; SAM sam; char s[maxN+100]; int du[2*maxN+100]; int head,tail,que[2*maxN+100]; int ge,out[maxN+100]; int main() { freopen("spoj7258.in","r",stdin); freopen("spoj7258.out","w",stdout); int i,j; scanf("%s\n",s+1); sam.insert(s); re(i,1,sam.idx)re(j,0,26-1)if(sam.sn[i].ch[j]) du[sam.sn[i].ch[j]]++; head=0;tail=-1; re(i,1,sam.idx)if(du[i]==0)que[++tail]=i; while(head<=tail) { int u=que[head++]; re(j,0,26-1)if(sam.sn[u].ch[j]) { int v=sam.sn[u].ch[j]; du[v]--; if(du[v]==0)que[++tail]=v; } } red(i,tail,0) { int u=que[i]; sam.sn[u].cnt=1; re(j,0,26-1)if(sam.sn[u].ch[j]) { int v=sam.sn[u].ch[j]; sam.sn[u].cnt+=sam.sn[v].cnt; } } for(int Q=gint();Q;Q--) { LL K=gll(); int u=1; ge=0; while(K!=0) { re(i,0,26-1) { int v=sam.sn[u].ch[i]; if(sam.sn[v].cnt<K) K-=sam.sn[v].cnt; else { out[++ge]=i; u=v; K--; break; } } } re(i,1,ge)printf("%c",out[i]+'a');printf("\n"); } return 0; }
hdu4622
【题意】
给定一个字符串,长度最长为2000,有至多10000组询问,每个询问给定一个区间,求出该区间内共有多少个不同的子串。
【解析】
离线出来。
先将区间排序,左端点从小到大,左端点相同就右端点从小到大。
相同的左端点可以归为一组,对每个后缀做SAM,因为最多只有2000个后缀,不超时。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=2000; const int maxQ=10000; int N; char s[maxN+100]; int Q; struct Tdata { int l,r,id; LL ans; }data[maxQ+100]; inline bool cmp(Tdata a,Tdata b){return (a.l==b.l)?a.r<b.r:a.l<b.l;} inline bool cmpid(Tdata a,Tdata b){return a.id<b.id;} struct SAM { struct Tnode { int ch[26],fail,l; inline void clear(){mmst(ch,0);fail=l=0;} }sn[2*maxN+100]; int idx,last; LL cnt; inline void clear(){cnt=0;sn[idx=last=1].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int z) { int end=newnode(); int p=last; last=end; sn[end].l=sn[p].l+1; for(;p && !sn[p].ch[z];p=sn[p].fail) { sn[p].ch[z]=end; if(p!=1) cnt+=LL(sn[p].l-sn[sn[p].fail].l); else cnt++; } if(!p) sn[end].fail=1; else { int q=sn[p].ch[z]; if(sn[p].l+1==sn[q].l) sn[end].fail=q; else { int nq=newnode(); sn[nq]=sn[q]; sn[nq].l=sn[p].l+1; sn[q].fail=sn[end].fail=nq; for(;p && sn[p].ch[z]==q;p=sn[p].fail)sn[p].ch[z]=nq; } } } }; SAM sam; int main() { freopen("hdu4622.in","r",stdin); freopen("hdu4622.out","w",stdout); int i,j; for(int Case=gint();Case;Case--) { scanf("%s\n",s+1); Q=gint(); re(i,1,Q)data[i].l=gint(),data[i].r=gint(),data[i].id=i; sort(data+1,data+Q+1,cmp); int r; re(i,1,Q) { if(data[i].l!=data[i-1].l){sam.clear();r=data[i].l-1;} re(j,r+1,data[i].r)sam.add(s[j]-'a'); r=data[i].r; data[i].ans=sam.cnt; } sort(data+1,data+Q+1,cmpid); re(i,1,Q)printf("%I64d\n",data[i].ans); } return 0; }
bzoj3879
【题意】
给一个字符串。有多个询问,每个询问有若干个后缀,问这些后缀的最长公共前缀。
【解析】
网上的解法是后缀树,在后缀树中求LCA。然后根据性质我们可以通过SAM构造出后缀树,这样可以避免普通后缀树构造算法的冗长。
但是我看到这道题就想到了后缀数组,可以利用求heigh时的单调性,用单调栈。
速度比后缀树快。。。
乱入了一个后缀数组代码。。。。。。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=500000; const LL Mod=23333333333333333LL; const int maxT=3000000; int N,M; char s[maxN+1000]; int sa[maxN+100],rank[maxN+100],height[maxN+100]; int wa[maxN+100],wb[maxN+100],table[maxN+100]; inline bool cmp(int *y,int a,int b,int len){return y[a]==y[b] && y[a+len]==y[b+len];} inline void da() { int i,j,k,p,tol,*x=wa,*y=wb; p=0;re(i,1,N)upmax(p,x[i]=s[i]); re(i,1,p)table[i]=0; re(i,1,N)table[x[i]]++; re(i,1,p)table[i]+=table[i-1]; red(i,N,1)sa[table[x[i]]--]=i; for(swap(x,y),p=x[sa[1]]=1,i=2;i<=N;i++)x[sa[i]]=cmp(y,sa[i],sa[i-1],0)?p:++p; for(j=1;p<N;j<<=1) { tol=0;re(i,N-j+1,N)y[++tol]=i; re(i,1,N)if(sa[i]-j>=1)y[++tol]=sa[i]-j; re(i,1,p)table[i]=0; re(i,1,N)table[x[y[i]]]++; re(i,1,p)table[i]+=table[i-1]; red(i,N,1)sa[table[x[y[i]]]--]=y[i]; for(swap(x,y),p=x[sa[1]]=1,i=2;i<=N;i++)x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p:++p; } re(i,1,N)rank[sa[i]]=i; for(i=1,k=0;i<=N-1;height[rank[i++]]=k) for(k=max(0,k-1),j=sa[rank[i]-1];s[i+k]==s[j+k];k++); } int lg2[maxN+100]; int RMQ[31][maxN+100]; inline int ask(int l,int r) { int h=lg2[r-l+1]; return min(RMQ[h][l],RMQ[h][r-two(h)+1]); } int t; int p[maxT+100];////注意范围 不是maxN int top; LL val[maxN+100],cnt[maxN+100]; inline LL solve() { LL res=0,sum=0;int i; top=0; re(i,2,t) { int v=ask(p[i-1]+1,p[i]); top++; val[top]=LL(v); cnt[top]=1; sum=(sum+LL(v))%Mod; while(top>=2 && val[top-1]>=val[top]) { sum=(sum-cnt[top-1]*(val[top-1]-val[top])%Mod)%Mod; val[top-1]=val[top]; cnt[top-1]+=cnt[top]; top--; } res=(res+sum)%Mod; } res=(res%Mod+Mod)%Mod; return res; } int main() { freopen("bzoj3879.in","r",stdin); freopen("bzoj3879.out","w",stdout); int i,j; N=gint();M=gint(); scanf("%s\n",s+1); s[++N]=' '; da(); lg2[1]=0;re(i,2,N)lg2[i]=lg2[i>>1]+1; re(i,1,N)RMQ[0][i]=height[i]; re(j,1,30)re(i,1,N)if(i+two(j-1)<=N) RMQ[j][i]=min(RMQ[j-1][i],RMQ[j-1][i+two(j-1)]); else RMQ[j][i]=RMQ[j-1][i]; re(i,1,M) { t=gint(); re(j,1,t)p[j]=gint(); re(j,1,t)p[j]=rank[p[j]]; sort(p+1,p+t+1); t=unique(p+1,p+t+1)-p-1; cout<<solve()<<endl; } return 0; }
spoj8222
【题意】
给定一个字符串,求出现次数最多的长度为 i 的子串的次数,i 的取值为 1 - len,len表示这个字符串的长度。
【解析】
表示刚开始以为是水题,然后直接用线段树。结果被spoj卡了,TLE。
膜拜cjl,详情见cjl论文。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=250000; struct SAM { struct Tnode { int ch[26],fail,l,size; inline void clear(){mmst(ch,0);fail=l=size=0;} }sn[2*maxN+100]; int idx,last; inline void clear(){sn[idx=last=1].clear();} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int z) { int end=newnode(); int p=last; last=end; sn[end].l=sn[p].l+1; for(;p && !sn[p].ch[z];p=sn[p].fail)sn[p].ch[z]=end; if(!p) sn[end].fail=1; else { int q=sn[p].ch[z]; if(sn[p].l+1==sn[q].l) sn[end].fail=q; else { int nq=newnode(); sn[nq]=sn[q]; sn[q].fail=sn[end].fail=nq; sn[nq].l=sn[p].l+1; for(;p && sn[p].ch[z]==q;p=sn[p].fail)sn[p].ch[z]=nq; } } //for(p=end;p;p=sn[p].fail)sn[p].size++; } int du[2*maxN+100],head,tail,que[2*maxN+100]; inline void insert(char *s,int N) { int i; clear(); re(i,1,N)add(s[i]-'a'); int p=1; re(i,1,N){p=sn[p].ch[s[i]-'a'];sn[p].size++;} re(i,1,idx)du[i]=0; re(i,2,idx)du[sn[i].fail]++; head=0;tail=-1; re(i,1,idx)if(du[i]==0)que[++tail]=i; while(head<=tail) { int u=que[head++],fa=sn[u].fail; if(fa!=1) { sn[fa].size+=sn[u].size; du[fa]--; if(du[fa]==0) que[++tail]=fa; } } } }; SAM sam; int N; char s[maxN+100]; int F[maxN+100]; int main() { freopen("spoj8222.in","r",stdin); freopen("spoj8222.out","w",stdout); int i; scanf("%s\n",s+1);N=strlen(s+1); sam.insert(s,N); re(i,2,sam.idx)upmax(F[sam.sn[i].l],sam.sn[i].size); red(i,N-1,1)upmax(F[i],F[i+1]); re(i,1,N)printf("%d\n",F[i]); return 0; }
【题意】
给定一个原始的字符串,有若干次操作,每次操作可以向该字符串末尾添加一个字符串或者询问某个子串在字符串中出现的次数。强制在线。
【解析】
我们求size的时候是沿祖先向上不断+1的。
这样很慢。
可以用LCT维护。
第一次写,代码比较冗长。
#include<cstdio> #include<cstdlib> #include<iostream> #include<fstream> #include<algorithm> #include<cstring> #include<string> #include<cmath> #include<queue> #include<stack> #include<map> #include<utility> #include<set> #include<bitset> #include<vector> #include<functional> #include<deque> #include<cctype> #include<climits> #include<complex> //#include<bits/stdc++.h>适用于CF,UOJ,但不适用于poj using namespace std; typedef long long LL; typedef double DB; typedef pair<int,int> PII; typedef complex<DB> CP; #define mmst(a,v) memset(a,v,sizeof(a)) #define mmcy(a,b) memcpy(a,b,sizeof(a)) #define re(i,a,b) for(i=a;i<=b;i++) #define red(i,a,b) for(i=a;i>=b;i--) #define fi first #define se second #define m_p(a,b) make_pair(a,b) #define SF scanf #define PF printf #define two(k) (1<<(k)) template<class T>inline T sqr(T x){return x*x;} template<class T>inline void upmin(T &t,T tmp){if(t>tmp)t=tmp;} template<class T>inline void upmax(T &t,T tmp){if(t<tmp)t=tmp;} const DB EPS=1e-9; inline int sgn(DB x){if(abs(x)<EPS)return 0;return(x>0)?1:-1;} const DB Pi=acos(-1.0); inline int gint() { int res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } inline LL gll() { LL res=0;bool neg=0;char z; for(z=getchar();z!=EOF && z!='-' && !isdigit(z);z=getchar()); if(z==EOF)return 0; if(z=='-'){neg=1;z=getchar();} for(;z!=EOF && isdigit(z);res=res*10+z-'0',z=getchar()); return (neg)?-res:res; } const int maxN=600000; struct SAM { struct Splay_Node { Splay_Node *son[2],*fa,*path_parent; int isRev,val,add; inline Splay_Node(){son[0]=son[1]=fa=path_parent=0;isRev=val=add=0;} inline void down() { if(isRev) { swap(son[0],son[1]); if(son[0])son[0]->isRev^=1; if(son[1])son[1]->isRev^=1; isRev=0; } if(add!=0) { if(son[0])son[0]->add+=add,son[0]->val+=add; if(son[1])son[1]->add+=add,son[1]->val+=add; add=0; } } }; Splay_Node *root; inline void Splay_Rotate(Splay_Node *x,int flag) { Splay_Node *y=x->fa,*z=y->fa; x->path_parent=y->path_parent;y->path_parent=0; if(x->son[flag])x->son[flag]->fa=y;y->son[flag^1]=x->son[flag]; x->son[flag]=y;y->fa=x; x->fa=z;if(z)z->son[y==z->son[1]]=x; } inline void Splay(Splay_Node *x) { Splay_Node *y,*z; while(x->fa) { y=x->fa;z=y->fa; if(z)z->down();if(y)y->down();if(x)x->down(); if(!z){Splay_Rotate(x,x==y->son[0]);break;} int L=(x==y->son[0]),R=(y==z->son[0]); if(L==R){Splay_Rotate(y,L);Splay_Rotate(x,R);continue;} if(L!=R){Splay_Rotate(x,L);Splay_Rotate(x,R);continue;} } x->down(); } inline void LCT_Access(Splay_Node *x) { Splay_Node *y; for(y=0;x;y=x,x=x->path_parent) { Splay(x); x->down(); if(x->son[1]){x->son[1]->fa=0;x->son[1]->path_parent=x;} x->son[1]=y; if(y){y->fa=x;y->path_parent=0;} } } inline void LCT_Evert(Splay_Node *x) { LCT_Access(x); Splay(x); x->isRev^=1; x->down(); } inline void LCT_Link(Splay_Node *u,Splay_Node *v) { LCT_Evert(u); Splay(u); u->path_parent=v; } inline void LCT_Cut(Splay_Node *u,Splay_Node *v) { LCT_Evert(root); LCT_Access(u); LCT_Access(v); Splay(u); u->path_parent=0; } inline void LCT_Add(Splay_Node *u) { LCT_Evert(root); LCT_Access(u); Splay(u); u->val++;u->add++; u->down(); } inline int LCT_Ask(Splay_Node *u) { Splay(u); return u->val; } struct Tnode { int ch[26],fail,l;Splay_Node *pos; inline void clear(){mmst(ch,0);fail=l=0;pos=new Splay_Node;} }sn[2*maxN+100]; int idx,last; inline void clear(){sn[idx=last=1].clear();root=sn[1].pos;} inline int newnode(){sn[++idx].clear();return idx;} inline void add(int z) { int end=newnode(); int p=last; last=end; sn[end].l=sn[p].l+1; for(;p && !sn[p].ch[z];p=sn[p].fail)sn[p].ch[z]=end; if(!p) { sn[end].fail=1; LCT_Link(sn[end].pos,sn[sn[end].fail].pos); } else { int q=sn[p].ch[z]; if(sn[p].l+1==sn[q].l) { sn[end].fail=q; LCT_Link(sn[end].pos,sn[sn[end].fail].pos); } else { int nq=newnode(); Splay_Node *temp=sn[nq].pos; sn[nq]=sn[q]; sn[nq].pos=temp; sn[nq].pos->val=LCT_Ask(sn[q].pos);//注意,常错写成 sn[nq].pos->val=sn[q].pos->val sn[nq].l=sn[p].l+1; //注意不要把pos全部复制过来,但是pos的val要复制 LCT_Cut(sn[q].pos,sn[sn[q].fail].pos); sn[q].fail=sn[end].fail=nq; LCT_Link(sn[q].pos,sn[sn[q].fail].pos); LCT_Link(sn[end].pos,sn[sn[end].fail].pos); LCT_Link(sn[nq].pos,sn[sn[nq].fail].pos); for(;p && sn[p].ch[z]==q;p=sn[p].fail)sn[p].ch[z]=nq; } } LCT_Add(sn[end].pos); } }; SAM sam; const int maxQL=3000000; char s[maxQL+1000]; inline void decodewithmark(char *s,int mark) { int i; re(i,0,strlen(s+1)-1) { mark=(mark*131+i)%strlen(s+1); swap(s[i+1],s[mark+1]); } } int main() { freopen("bzoj2555.in","r",stdin); freopen("bzoj2555.out","w",stdout); int Q=gint(),i,mark=0; sam.clear(); scanf("%s\n",s+1); re(i,1,strlen(s+1))sam.add(s[i]-'A'); for(;Q;Q--) { char type[20]; scanf("%s%s\n",type+1,s+1); decodewithmark(s,mark); if(type[1]=='A') re(i,1,strlen(s+1))sam.add(s[i]-'A'); else { int p=1,len,res=-1; re(i,1,strlen(s+1)) { int z=s[i]-'A'; if(sam.sn[p].ch[z]) p=sam.sn[p].ch[z]; else {res=0;break;} } if(res!=0)res=sam.LCT_Ask(sam.sn[p].pos); printf("%d\n",res); mark^=res; } } return 0; }