首页 > 代码库 > 后缀自动机总结
后缀自动机总结
第一次写一个算法的总结
poj 1509 Glass Beads
题目要求求一个字符串的最小表示,在SAM上面走,可以找到这个字符串的所以字串,这样我们可以把 string str 重复一次,
然后在SAM上面往最小的走 len 步,这样得到的就是题目要求的
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<vector>#include<set>#include<stack>#include<map>#include<ctime>#include<bitset>#define LL long long#define mod 1000000007#define maxn 20010#define pi acos(-1.0)#define eps 1e-6using namespace std;struct SAM{ SAM *pre,*son[26] ; int len ,g ;}que[maxn],*root,*tail,*b[maxn];int tot ;void add(int c ,int l){ SAM *p = tail,*np=&que[tot++] ; np->len=l;tail=np ; while(p&&p->son[c]==NULL)p->son[c]=np,p=p->pre ; if(p==NULL) np->pre = root ; else { SAM *q = p->son[c] ; if(p->len+1==q->len)np->pre = q ; else { SAM *nq = &que[tot++] ; *nq=*q ; nq->len = p->len+1; np->pre=q->pre=nq; while(p&&p->son[c]==q) p->son[c]=nq,p=p->pre; } }}char str[maxn];void init(int n ){ tot=0; for(int i = 0 ; i < n ;i++) { que[i].g = 0 ; que[i].pre=NULL; memset(que[i].son,0,sizeof(que[i].son)) ; } root=tail=&que[tot++] ;}int solve(int n ){ for( int i = 1 ; i <= n ;i++) { add(str[i-1]-‘a‘,i) ; } for( int i = 1 ; i <= n ;i++) { add(str[i-1]-‘a‘,i+n) ; } SAM *now = &que[0] ; for(int i = 0 ; i < n ;i++) { for(int j = 0 ;j < 26 ;j++) { if(now->son[j] != NULL) { now = now->son[j] ; break ; } } } return now->len ;}int main(){ int m,n,i,j,k; int u,v,c; while(scanf("%d",&n) != EOF) { while(n--) { scanf("%s",str) ; m = strlen(str) ; init(m*2); printf("%d\n",solve(m)-m+1) ; } } return 0 ;}
hdu 4270 Dynamic Lover
题意 :1.插入一个字符串,2 .询问1-len开头的,长度为len的(如果有的话,没有就是后缀了),最小子串是谁,3,删除末尾 长为 len 的子串
对于询问,和上面一样,走len步,这里有可能是长度不为len,所以把后缀标记一下就好了。对于 1,直接插入,对于 3,
我们在后缀自动机增加标记 *del,这个指向一个数组。而且它复制出来的节点和它指向地址是一样的,这样删除就只要标记一个就好了。
还有在插入的时候,用pos[i]记录长度为i的字符在SAM位置。删的时候,就从n-len+1删除到n就好了,注意删除后的末尾节点也要改变
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<vector>#include<set>#include<stack>#include<map>#include<ctime>#include<bitset>#define LL long long#define mod 1000000007#define maxn 300010#define INF 0x3f3f3f3fusing namespace std;struct SAM{ SAM *pre,*son[26] ; int e,id; bool *del; int len ,g; void init() { pre=NULL ; e=0; memset(son,NULL,sizeof(son)) ; }}que[maxn],*root,*tail,*b[maxn];int tot ,tt ;int kk ,n ,cnt,pos[maxn];bool num[maxn];void add(int c ,int l){ que[tot].init(); SAM *p = tail,*np=&que[tot++] ; np->del = &num[tt++]; np->len=np->id=l;tail=np ; pos[l]=tot-1; while(p&&(p->son[c]==NULL||(*p->son[c]->del)))p->son[c]=np,p=p->pre ; if(p==NULL) np->pre = root ; else { SAM *q = p->son[c] ; if(p->len+1==q->len)np->pre = q ; else { SAM *nq = &que[tot++] ; *nq=*q ; nq->len = p->len+1; np->pre=q->pre=nq; while(p&&p->son[c]==q) p->son[c]=nq,p=p->pre; } }}int dfs(SAM *p ,int len){ if(len==kk) return p->id-kk+1 ; if(p->e==cnt) return n-len+1 ; for(int i = 0 ; i < 26 ;i++)if(p->son[i]&&!(*p->son[i]->del)) return dfs(p->son[i],len+1) ;}void Delete(int len ){ for(int i = n-len+1 ; i <= n ;i++)*(que[pos[i]].del)=true; n -= len ; tail = &que[pos[n]];}void init(){ tot=0; tt=0; cnt=0; memset(num,0,sizeof(num)) ; que[tot].init(); root=tail=&que[tot++] ; root->del = &num[tt++] ;}char a[maxn] ;int main(){ int i ,m,k ,j ; int T ,case1=0,len; while(scanf("%s",a) !=EOF) { n = strlen(a) ; init(); for( i = 1 ; i <= n ;i++) add(a[i-1]-‘a‘,i); scanf("%d",&m) ; while(m--) { scanf("%d",&k) ; if(k==1) { scanf("%s",a) ; len=strlen(a) ; for( i = 0 ; i < len ;i++) add(a[i]-‘a‘,++n) ; } else if(k==2) { SAM *p ; cnt++; p = tail ; while(p != NULL &&p != root){ p->e = cnt ; p = p->pre ; } root->e=0; p = root ; scanf("%d",&kk) ; printf("%d\n",dfs(p,0)) ; } else { scanf("%d",&j) ; Delete(j); } } } return 0 ;}
hdu 4416 Good Article Good sentence
题意:给出 string a ,和 string b[] ,询问a一共有多少个不同的子串 不是b[i]的子串
对a建立 SAM,然后经行拓扑排序。然后b[i]去上面匹配,对于节点 p 我们记录所有b[i]中长度最长的匹配,p->g
因为父辈的子串由儿子那里得到,我们从后面开始计算答案,然后把p->g传给父亲。
计算的时候,对于节点 p ,以它为结尾的字串由p->len 个,它和父亲重叠的部分是 p->pre->len ,
和b[i]字串重叠最长p->g ,所以得到合法子串 p->len - max(p->pre->len ,p->g ) ;
注意SAM得到的字串都是不重复的,所以计算出来的就是所以不重复的字串。
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<vector>#include<set>#include<stack>#include<map>#include<ctime>#include<bitset>#define LL long long#define mod 1000000007#define maxn 200010#define INF 0x3f3f3f3fusing namespace std;struct SAM{ SAM *pre,*son[26] ; int len ,g;}que[maxn],*root,*tail,*b[maxn];int tot ;void add(int c ,int l){ SAM *p = tail,*np=&que[tot++] ; np->len=l;tail=np ; while(p&&p->son[c]==NULL)p->son[c]=np,p=p->pre ; if(p==NULL) np->pre = root ; else { SAM *q = p->son[c] ; if(p->len+1==q->len)np->pre = q ; else { SAM *nq = &que[tot++] ; *nq=*q ; nq->len = p->len+1; np->pre=q->pre=nq; while(p&&p->son[c]==q) p->son[c]=nq,p=p->pre; } }}void init(int n ){ tot=0; for( int i = 0 ; i <= n ;i++) { que[i].pre = NULL ; que[i].g=0; memset(que[i].son,0,sizeof(que[i].son)) ; } root=tail=&que[tot++] ;}char a[maxn] ;int C[maxn] ;LL solve(int n ,int m){ memset(C,0,sizeof(C)) ; for(int i = 0 ; i < tot;i++) C[que[i].len]++ ; for(int i = 1 ; i <= n ;i++)C[i] += C[i-1] ; for(int i = 0 ; i < tot ;i++)b[--C[que[i].len]] =&que[i] ; SAM *p ; int tmp,len,i; while(m--) { p = &que[0] ; tmp=0; scanf("%s",a) ; len = strlen(a) ; for( i = 0 ; i < len ;i++) { if(p->son[a[i]-‘a‘]) { tmp++ ; p = p->son[a[i]-‘a‘]; } else { while(p && p->son[a[i]-‘a‘]==NULL) p = p->pre ; if(p==NULL) { tmp=0; p = &que[0] ; } else { tmp = p->len+1; p = p->son[a[i]-‘a‘]; } } p->g = max(p->g,tmp) ; } } LL ans = 0 ; for( i = tot-1 ; i > 0 ;i--) { p = b[i]; if(p->pre){ tmp = max(p->pre->len,p->g); p->pre->g = max(p->pre->g,p->g ) ; } else tmp = p->g ; if(p->len<tmp)tmp=p->len; ans += p->len-tmp ; } return ans;}int main(){ int i , n, m,k ,j ; int T ,case1=0; cin >> T ; while( T--) { scanf("%d",&m) ; scanf("%s",a); n = strlen(a) ; init(n*2); for( i = 1 ; i <= n ;i++) add(a[i-1]-‘a‘,i); printf("Case %d: ",++case1); cout << solve(n,m) << endl; } return 0 ;}
hdu 4622 Reincarnation
题意:给出 string a ,给出l,r区间,询问这个居间不同字串个数
离线,记录 l,下所以的询问 r 。对于不同l ,每次我们都重构SAM
查找的时候,对于节点 p , 以它结尾的有p->len 个子串,和父亲重叠的 p->pre->len
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<queue>#include<vector>#include<set>#include<stack>#include<map>#include<ctime>#include<bitset>#define LL long long#define mod 1000000007#define maxn 10010#define INF 0x3f3f3f3fusing namespace std;struct SAM{ SAM *pre,*son[26] ; int len ,g ,num; bool vi;}que[maxn],*root,*tail,*b[maxn];int tot ;void add(int c ,int l){ SAM *p = tail,*np=&que[tot++] ; np->len=l;tail=np ; while(p&&p->son[c]==NULL)p->son[c]=np,p=p->pre ; if(p==NULL) np->pre = root ; else { SAM *q = p->son[c] ; if(p->len+1==q->len)np->pre = q ; else { SAM *nq = &que[tot++] ; *nq=*q ; nq->len = p->len+1; np->pre=q->pre=nq; while(p&&p->son[c]==q) p->son[c]=nq,p=p->pre; } }}char str1[10010];void init(int n ){ tot=0; for(int i = 0 ; i <= n ;i++) { memset(que[i].son,0,sizeof(que[i].son)) ; que[i].num = 0 ; que[i].pre=NULL; } root=tail=&que[tot++] ;}int find(){ int ans=0; for(int i =1 ; i < tot;i++) { SAM *p = que[i].pre ; if(p != NULL ) ans += que[i].len-p->len ; else ans += que[i].len ; } return ans;}struct node{ int R,id ; bool operator<(const node&s) const { return R < s.R ; }};vector<node>qe[maxn] ;int ans[maxn];int main(){ int m,n,i,j; int u,v,c,tmp; int T ,len; node a; cin >> T ; while(T--) { scanf("%s",str1+1) ; scanf("%d",&m) ; n = strlen(str1+1) ; for( i =1 ; i <= n ;i++) qe[i].clear(); for( i = 1 ; i <= m ;i++){ scanf("%d%d",&u,&v); a.R =v ; a.id= i ; qe[u].push_back(a) ; } for( i =1 ; i <= n ;i++) sort(qe[i].begin(),qe[i].end()); for( i = 1 ; i <= n ;i++) { len = qe[i].size(); if(len==0) continue ; u = 0 ; v=1; init(n*2); for( j = i ; j <= n ;j++) { add(str1[j]-‘a‘,v++); while(u < len && qe[i][u].R == j) { ans[qe[i][u].id] = find(); u++; } if(u==len) break ; } } for( i = 1 ; i <= m ;i++) printf("%d\n",ans[i]); } return 0 ;}
后缀自动机总结