回文自动机 马拉车 后缀自动机 后缀数组 AC自动机 EXKMP KMP 字典树 字符串匹配shiftand算法 字符串哈希
手写哈希
#include<bits/stdc++.h> using namespace std; struct Hash { static const int MOD = 1999997; static const int N = 1e6; int head[MOD + 10], nx[N], top; int hs[N], id[N]; void init() { memset(head, -1, sizeof head); top = 0; } void insert(int x, int y) { int k = x % MOD; hs[top] = x; id[top] = y; nx[top] = head[k]; head[k] = top++; } int find(int x) { int k = x % MOD; for (int i = head[k]; i != -1; i = nx[i]) { if (hs[i] == x) { return id[i]; } } return -1; } }hs; int main(){ } hash
回文自动机模板
#include<bits/stdc++.h> using namespace std; struct PAM{ #define ll long long #define LL long long static const int maxn=3e5+10; static const int num=27; char ss[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; //LL ans; void inint(){ c=0; last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c]='a'+x; c++; int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; //ans++; } cnt[last=ch[p][x]]++; } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; } }pam; const int maxn=1e5+10; char ss[maxn]; int main(){ }
回文自动机 本质不同回文串个数 tot-1;
#include<bits/stdc++.h> #define ll long long #define LL long long using namespace std; const int maxn=2e5+10; const int num=26; char ss[maxn]; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; struct PAM{ int last,tot; ll ans; void inint(){ last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; } cnt[last=ch[p][x]]++; } void count(){ // bu zhi bu tong hui wen ge shu for(int i=tot;i>=0;i--){ cnt[fail[i]]+=cnt[i]; cnt[fail[i]]%=51123987; } } ll count_tot(){ ll w=0; count(); for(int i=2;i<=tot;i++) w+=cnt[i]; return w; } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; } }pam; int main(){ //int x; scanf("%d",&x); scanf("%s",ss); int l=strlen(ss); pam.inint(); for(int i=0;i<l;i++) { pam.add(ss[i]-'a',i); if(i!=0) printf(" "); printf("%d",pam.tot-1); } }
回文自动机 字符串l-r 区间本质不同回文串个数
#include<bits/stdc++.h> #define ll long long #define LL long long using namespace std; const int maxn=1010; struct PAM{ static const int maxn=1e3+10; static const int num=26; char ss[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; ll ans; void inint(){ last=0; tot=0; c=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c++]=x+'a'; int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; } cnt[last=ch[p][x]]++; } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; c=0; } }pam; char ss[maxn]; int a[maxn][maxn]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",ss); int q; scanf("%d",&q); int n=strlen(ss); for(int i=0;i<n;i++){ pam.clear(); pam.inint(); for(int j=i;j<n;j++){ pam.add(ss[j]-'a',j-i); a[i][j]=pam.tot-1; } } while(q--){ int l,r; scanf("%d %d",&l,&r); printf("%d\n",a[l-1][r-1]); } } }
回文自动机 回文串权值问题
#include<bits/stdc++.h> #define ll long long #define LL long long using namespace std; const int maxn=1010000; struct PAM{ static const int maxn=3e5+10; static const int num=26; char ss[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; ll ans=0; void inint(){ last=0; tot=0; c=0; ans=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c++]=x+'a'; int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; } cnt[last=ch[p][x]]++; } void dfs(int x){ for(int i=0;i<num;i++){ if(ch[x][i]){ int w=ch[x][i]; //cout<<w<<" "<<cnt[w]<<" "<<len[w]<<endl; ans=max(1ll*cnt[w]*len[w],ans); dfs(w); } } } ll slove(){ for(int i=tot;i>=0;i--) cnt[fail[i]]+=cnt[i]; dfs(1); dfs(0); return ans; } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; c=0; } }pam; char ss[maxn]; int a[maxn][maxn]; int main(){ scanf("%s",ss); pam.inint(); int n=strlen(ss); for(int i=0;i<n;i++){ pam.add(ss[i]-'a',i); } printf("%lld\n",pam.slove()); }
hdu 5394 回文自动机上删点 加点操作 回文自动机上记忆化搜索
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<vector> #include<stdio.h> #include<math.h> using namespace std; struct PAM{ #define ll long long #define LL long long static const int maxn=2e6+10; static const int num=27; char ss[maxn]; int relast[maxn]; int retot[maxn]; ll sum[maxn]; bool check[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; void inint(){ c=0; last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c]='a'+x; c++; relast[pos]=last;// retot[pos]=tot;// int p=get_fail(last,pos); if(!ch[p][x]){ check[pos]=1;// len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; } cnt[last=ch[p][x]]++; } void re(int x,int pos){ c--; last=relast[pos]; tot=retot[pos]; int p=get_fail(last,pos); if(check[pos]==1){ check[pos]=0; len[++tot]=0; fail[tot]=0; ch[p][x]=0; sum[tot]=0; } cnt[last=ch[p][x]]--; last=relast[pos]; tot=retot[pos]; relast[pos]=0; retot[pos]=0; } ll dfs(int x){ if(x==0 || x==1) return 0; if(sum[x]!=0) return sum[x]; return sum[x]=dfs(fail[x])+len[x]; } ll work(){ return dfs(last); } }pam; #define ll long long const int maxn=2e6+10; //char ss[maxn]; ll ans=0; vector<pair<int,char> > vs[maxn]; void dfs(int x,int tot){ for(int i=0;i<vs[x].size();i++){ pam.add(vs[x][i].second,tot); ans+=pam.work(); dfs(vs[x][i].first,tot+1); pam.re(vs[x][i].second,tot); } } struct FastIO { static const int S = 4e6; int wpos; char wbuf[S]; FastIO() : wpos(0) {} inline int xchar() { static char buf[S]; static int len = 0, pos = 0; if (pos == len) pos = 0, len = fread(buf, 1, S, stdin); if (pos == len) exit(0); return buf[pos++]; } inline int xuint() { int c = xchar(), x = 0; while (c <= 32) c = xchar(); for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0'; return x; } inline int xint() { int s = 1, c = xchar(), x = 0; while (c <= 32) c = xchar(); if (c == '-') s = -1, c = xchar(); for (; '0' <= c && c <= '9'; c = xchar()) x = x * 10 + c - '0'; return x * s; } inline void xstring(char *s) { int c = xchar(); while (c <= 32) c = xchar(); for (; c > 32; c = xchar()) * s++ = c; *s = 0; } inline void wchar(int x) { if (wpos == S) fwrite(wbuf, 1, S, stdout), wpos = 0; wbuf[wpos++] = x; } inline void wint(int x) { if (x < 0) wchar('-'), x = -x; char s[24]; int n = 0; while (x || !n) s[n++] = '0' + x % 10, x /= 10; while (n--) wchar(s[n]); wchar('\n'); } inline void wstring(const char *s) { while (*s) wchar(*s++); } ~FastIO() { if (wpos) fwrite(wbuf, 1, wpos, stdout), wpos = 0; } } io; int main(){ int T; T=io.xint(); //cin>>T; while(T--){ pam.inint(); ans=0; int n; n=io.xint(); //cin>>n; for(int i=1;i<=n;i++){ char x; int y; x=io.xchar(); y=io.xint(); //cin>>x>>y; vs[y].push_back({i,x}); } dfs(0,0); printf("%lld\n",ans); for(int i=0;i<=n;i++) vs[i].clear(); // pam.clear(); } }
洛谷 最长双回文串
#include<bits/stdc++.h> using namespace std; struct PAM{ #define ll long long #define LL long long static const int maxn=3e5+10; static const int num=27; char ss[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; LL ans; void inint(){ c=0; last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c]='a'+x; c++; int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; ans++; } cnt[last=ch[p][x]]++; } int work(){ return len[last]; } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; } }pam; const int maxn=2e5+10; char ss[maxn]; int ans[maxn]; int main(){ scanf("%s",ss); int l=strlen(ss); pam.inint(); for(int i=0;i<l;i++){ pam.add(ss[i],i); ans[i]+=pam.work(); } pam.clear(); pam.inint(); for(int i=l-1;i>=0;i--){ pam.add(ss[i],l-1-i); if(i-1>=0) ans[i-1]+=pam.work(); } int x=0; for(int i=1;i<l-1;i++){ x=max(ans[i],x); } printf("%d\n",x); }
洛谷 拉拉队排练
#include<bits/stdc++.h> using namespace std; int numm[2000005]; struct PAM{ #define ll long long #define LL long long static const int maxn=2e6+10; static const int num=27; char ss[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; LL ans; void inint(){ c=0; last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c]='a'+x; c++; int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; ans++; } cnt[last=ch[p][x]]++; } void count(){ for(int i=tot;i>=2;i--){ cnt[fail[i]]+=cnt[i]; if(len[i]%2==1){ numm[len[i]]+=cnt[i]; } } } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; } }pam; const int maxn=2e6+10; const int mod=19930726; #define ll long long char ss[maxn]; int quick(int x,int n){ int ans=1; while(n){ if(n&1) ans=1ll*ans*x%mod; x=1ll*x*x%mod; n=n/2; } return ans; } int main(){ ll n,k; scanf("%lld %lld",&n,&k); scanf("%s",ss); pam.inint(); for(int i=0;i<n;i++)pam.add(ss[i],i); pam.count(); ll ans=1; ll w=0; for(int i=n;i>=1;i--){ //cout<<numm[i]<<endl; if(i%2==1) w+=numm[i]; if(i%2==1 && k){ if(numm[i]<=k){ ans=1ll*ans*quick(i,numm[i])%mod; k-=numm[i]; } else { ans=1ll*ans*quick(i,k)%mod; k=0; } } } if(w<k) printf("-1\n"); else printf("%lld\n",ans); }
hdu 5157
#include<bits/stdc++.h> using namespace std; struct PAM{ #define ll long long #define LL long long static const int maxn=2e5+10; static const int num=27; char ss[maxn]; int c=0; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; int sum[maxn]; //LL ans; void inint(){ c=0; last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ ss[c]='a'+x; c++; int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; //ans++; } cnt[last=ch[p][x]]++; } int dfs(int x){ if(x==0 || x==1) return 0; if(sum[x]!=0) return sum[x]; return sum[x]=dfs(fail[x])+1; } int work(){ return dfs(last); } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; sum[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=0; } }pam; #define ll long long const int maxn=1e5+10; char ss[maxn]; ll a[maxn]; ll b[maxn]; ll sumb[maxn]; int main(){ while(scanf("%s",ss)!=EOF){ int l=strlen(ss); pam.inint(); for(int i=0;i<l;i++){ pam.add(ss[i]-'a',i); a[i]=pam.work(); } pam.clear(); pam.inint(); for(int i=l-1;i>=0;i--){ pam.add(ss[i]-'a',l-1-i); b[i]=pam.work(); } for(int i=l-1;i>=0;i--){ sumb[i]=sumb[i+1]+b[i]; } ll ans=0; for(int i=0;i<l;i++){ ans+=1ll*a[i]*sumb[i+1]; } printf("%lld\n",ans); for(int i=0;i<=l;i++) a[i]=b[i]=sumb[i]=0; pam.clear(); } }
hdu 6599 回文自动机 哈希 回文串 (l-r)回文 (l-(l+r)/2)回文)
#include<bits/stdc++.h> #define ll long long #define LL long long using namespace std; const int maxn=3e5+10; const int mod=1e9+7; const int h1=222333; char ss[maxn]; int a[maxn]; int ha[maxn]; int hb[maxn]; int hh[maxn]; struct PAM{ #define ll long long #define LL long long static const int maxn=3e5+10; static const int num=27; int fail[maxn],cnt[maxn],len[maxn],ch[maxn][num]; int last,tot; LL ans; void inint(){ last=0; tot=0; fail[0]=fail[1]=1; len[0]=0; len[1]=-1; tot++; } int get_fail(int p,int pos){ while(ss[pos-len[p]-1]!=ss[pos]) p=fail[p]; return p; } void add(int x,int pos){ int p=get_fail(last,pos); if(!ch[p][x]){ len[++tot]=len[p]+2; fail[tot]=ch[get_fail(fail[p],pos)][x]; ch[p][x]=tot; ans++; } cnt[last=ch[p][x]]++; } void count(){ // bu zhi bu tong hui wen ge shu for(int i=tot;i>=0;i--){ cnt[fail[i]]+=cnt[i]; } } void dfs(int x,int pos){ for(int i=0;i<num;i++){ if(ch[x][i]){ if(pos==0) ha[pos]=(1ll*i)%mod; else ha[pos]=(1ll*ha[pos-1]*h1+i)%mod; if(pos==0) hb[pos]=(1ll*i*hh[pos])%mod; else hb[pos]=(1ll*i*hh[pos]+hb[pos-1])%mod; int k=ch[x][i]; if(ha[pos]==hb[pos]){ a[len[k]]+=cnt[k]; } dfs(k,pos+1); } } } void work(){ dfs(0,0); dfs(1,0); } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; len[i]=0; cnt[i]=0; for(int j=0;j<num;j++) ch[i][j]=0; } last=tot=ans=0; } }pam; int main(){ hh[0]=1; for(int i=1;i<maxn;i++) hh[i]=1ll*hh[i-1]*h1%mod; while(~scanf("%s",ss)){ int l=strlen(ss); pam.inint(); //cout<<l<<endl; for(int i=0;i<l;i++) pam.add(ss[i]-'a',i); pam.count(); pam.work(); for(int i=1;i<=l;i++){ if(i!=1) printf(" "); printf("%d",a[i]); a[i]=0; }printf("\n"); pam.clear(); } }
马拉车 最长回文
#include<bits/stdc++.h> const int maxn=1e6+10; using namespace std; int p[maxn*2+10]; string manacher(string ss) { string tt="$#"; for(int i=0;i<ss.size();i++){tt+=ss[i]; tt+="#";} int mx=0; int id=0; int mid=0; int ls=0; for(int i=1;i<tt.size();i++) { if(mx>i) p[i]=min(p[2*id-i],mx-i); else p[i]=1; while(tt[i+p[i]]==tt[i-p[i]]) p[i]++; if(i+p[i]>mx) { mx=i+p[i]; id=i; } if(ls<p[i]) { ls=p[i]; mid=i; } } for(int i=1;i<tt.size();i++) p[i]=0; return ss.substr((mid - ls) / 2, ls - 1); } char a[maxn]; int main() { while(scanf("%s",&a)!=EOF) { string ss=""; int l=strlen(a); for(int i=0;i<l;i++) ss+=a[i]; ss=manacher(ss); cout<<ss.size()<<endl; } }
后缀自动机
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM{ #define ll long long #define LL long long static const int maxn=1e5+10; static const int MAXN=maxn; //大小为字符串长度两倍 static const int LS=26; int tot,last,ch[MAXN][LS],fa[MAXN],len[MAXN]; int num[MAXN]; void init(){ last=tot=1; len[1] = 0; memset(ch[1],0,sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1;//cnt[last] = 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else{ int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else{ int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void use(){ ll ans=0; for(int i=2;i<=tot;i++) { ans+=len[i]-len[fa[i]]; } printf("%lld\n",ans); } }sam; const int maxn=1e6+10; char ss[maxn]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.use(); } }
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM // /* https://www.cnblogs.com/weeping/p/7516063.html */ { static const int MAXN = 50000<<1;//大小为字符串长度两倍 static const int LetterSize = 300; int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; int num[MAXN]; void init( void) { last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1;//cnt[last] = 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else { int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void use(){ ll ans=0; for(int i=2;i<=tot;i++) { ans+=len[i]-len[fa[i]]; } printf("%lld\n",ans); } }sam; const int maxn=1e6+10; char ss[maxn]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.use(); } }
给定一个只包含小写字母的字符串S, 请你求出 S 的所有出现次数不为1的子串的出现次数乘上该子串长度的最大值 (次数一样 取最大长度) (后缀自动机拓扑排序模板)
// luogu-judger-enable-o2 #include<bits/stdc++.h> #define ll long long using namespace std; struct SAM // /* https://www.cnblogs.com/weeping/p/7516063.html */ { static const int MAXN = 1000000<<1;//大小为字符串长度两倍 static const int LetterSize = 26; int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; int num[MAXN]; //int sum[MAXN], tp[MAXN], cnt[MAXN]; //sum,tp用于拓扑排序,tp为排序后的数组 void init( void) { last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1;//cnt[last] = 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else { int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } /*void toposort( void){ for(int i = 1; i <= len[last]; i++) sum[i] = 0; for(int i = 1; i <= tot; i++) sum[len[i]]++; for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1]; for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i; for(int i = tot; i; i--) cnt[fa[tp[i]]] += cnt[tp[i]]; }*/ vector<int> vs[MAXN]; ll ans=0; void build(){ for(int i=2;i<=tot;i++) { vs[fa[i]].push_back(i); } //for(int i=1;i<=tot;i++) num[i]=1; } void dfs(int u){ for(int i=0;i<vs[u].size();i++){ dfs(vs[u][i]); num[u]+=num[vs[u][i]]; } if(num[u]!=1) ans=max(ans,1ll*num[u]*len[u]); //cout<<u<<" "<<ans<<endl; } } sam; const int maxn=1e6+10; char ss[maxn]; int main(){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.build(); sam.dfs(1); printf("%lld\n",sam.ans); }
本质不同字符串个数(模板题)New Distinct Substrings SPOJ - SUBST1
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM{ static const int MAXN = 50000<<1;//大小为字符串长度两倍 static const int LetterSize = 300; int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; int num[MAXN]; void init( void) { last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1;//cnt[last] = 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else { int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void use(){ ll ans=0; for(int i=2;i<=tot;i++) { ans+=len[i]-len[fa[i]]; } printf("%lld\n",ans); } }sam; const int maxn=1e6+10; char ss[maxn]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.use(); } }
hdu 4622 本质不同字符串个数 后缀自动机的清空
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM { static const int MAXN = 50000<<1;//大小为字符串长度两倍 static const int LetterSize = 27; int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; int num[MAXN]; void init( void){ last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1;//cnt[last] = 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else { int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void use(){ ll ans=0; for(int i=2;i<=tot;i++) { ans+=len[i]-len[fa[i]]; } printf("%lld\n",ans); } }sam; const int maxn=1e6+10; char ss[maxn]; int dp[2005][2005]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",ss); int q; scanf("%d",&q); while(q--){ sam.init(); int l,r; scanf("%d %d",&l,&r); for(int i=l;i<=r;i++) sam.add(ss[i-1]-'a'); sam.use(); } } return 0; }
两字符串的最长相同字串
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM{ static const int MAXN = 250010<<1;//大小为字符串长度两倍 static const int LetterSize = 300; int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; int num[MAXN]; void init( void){ last = tot = 1; len[1] = 0; len[0]=0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1; memset(ch[np],0,sizeof ch[np]); while(p&&!ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else{ int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else{ int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } }sam; const int maxn=1e6+10; char ss[maxn]; char tt[maxn]; int main(){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); scanf("%s",tt); int r=strlen(tt); int ans=0; int p=1; int d=0; for(int i=0;i<r;i++){ if(sam.ch[p][tt[i]-'a']){ p=sam.ch[p][tt[i]-'a']; d++; } else { while(sam.ch[p][tt[i]-'a']==0 && p!=1){ p=sam.fa[p]; d=sam.len[p]; } if(sam.ch[p][tt[i]-'a']) { p=sam.ch[p][tt[i]-'a']; d++;} } //cout<<d<<endl; ans=max(ans,d); } printf("%d\n",ans); }
hdu 6583 后缀自动机 + dp http://acm.hdu.edu.cn/showproblem.php?pid=6583
#pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize(4) #include<bits/stdc++.h> #define ll long long #define LL long long using namespace std; struct SAM { static const int MAXN = 200010<<1;//大小为字符串长度两倍 static const int LetterSize = 26; int tot, last, ch[MAXN][LetterSize], fa[MAXN], len[MAXN]; int num[MAXN]; void init( void) { last = tot = 1; len[1] = 0; fa[1]=0; memset( ch, 0, sizeof(ch)); memset( fa,0,sizeof(fa)); memset( len,0,sizeof(len)); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else { int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } }sam; const int maxn=2e5+10; char ss[maxn]; ll dp[maxn]; int main(){ while(~scanf("%s",ss)){ int p,q; scanf("%d %d",&p,&q); int l=strlen(ss); sam.init(); int now=1; int j=0; for(int i=0;i<l;i++) dp[i]=1e18; for(int i=0;i<l;i++){ if(i==0) dp[i]=p; else dp[i]=min(dp[i],dp[i-1]+p); int x=ss[i]-'a'; while( (!sam.ch[now][x] || i-j+1>=j+1) &&j<=i ){ sam.add(ss[j++]-'a'); while(now && sam.len[sam.fa[now]]>=i-j) now=sam.fa[now]; if(!now) now=1; } now=sam.ch[now][x]; while(now && sam.len[sam.fa[now]]>=i-j+1) now=sam.fa[now]; if(!now) now=1; if(i>=j){ dp[i]=min(dp[i],dp[j-1]+q); } // cout<<dp[i]<<" "<<i<<" "<<j<<endl; } printf("%lld\n",dp[l-1]); } }
vjudge C - Lexicographical Substring Search SPOJ - SUBLEX 拓扑排序 子串第K大 (不包含重复的) 注意 1-tot 不是拓扑序列
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM{ #define ll long long #define LL long long static const int maxn=1000010<<1;//大小为字符串长度两倍 static const int LS=27; int tot, last, ch[maxn][LS], fa[maxn], len[maxn]; int num[maxn]; void init( void){ last = tot = 1; len[1] = 0; len[0]=0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1; memset(ch[np],0,sizeof ch[np]); while(p&&!ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else{ int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else{ int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } // 1-tot 不是拓扑序列 vector<pair<int,int> > vs[maxn]; ll dp[maxn]; int duu[maxn]; void work(){ for(int i=1;i<=tot;i++){ for(int j=0;j<LS;j++){ if(ch[i][j]){ vs[i].push_back({ch[i][j],j}); duu[ch[i][j]]++; } } } queue<pair<int,int> > qu; vector<int> vq; qu.push({1,0}); while(!qu.empty()){ int p=qu.front().first; qu.pop(); vq.push_back(p); for(int j=0;j<vs[p].size();j++){ int u=vs[p][j].first; duu[u]--; if(duu[u]==0){ qu.push(vs[p][j]); } } } for(int i=vq.size()-1;i>=0;i--){ int w=vq[i]; dp[w]=1; for(int j=0;j<LS;j++){ if(ch[w][j]){ int x=ch[w][j]; dp[w]+=dp[x]; } } } } void find(int p,ll k){ for(int i=0;i<LS;i++){ if(ch[p][i]){ int x=ch[p][i]; if(k<=dp[x]){ printf("%c",i+'a'); k--; if(k){ find(x,k); } return ; } else { k-=dp[x]; } } } } }sam; const int maxn=1e6+10; char ss[maxn]; char tt[maxn]; int main(){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.work(); int q; scanf("%d",&q); while(q--){ int k; scanf("%d",&k); sam.find(1,k);printf("\n"); } }
vjudge B - Substrings SPOJ - NSUBSTR 本质字符串个数 (直接跑后缀树) 利用后缀树的性质优化区间修改 单点最大值
#include<bits/stdc++.h> #define ll long long using namespace std; int a[1000006]; struct SAM{ static const int MAXN = 250010<<1;//大小为字符串长度两倍 static const int LS = 27; int tot, last, ch[MAXN][LS], fa[MAXN], len[MAXN]; int num[MAXN]; void init( void){ last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; num[tot]=1; len[np] = len[p] + 1;//cnt[last] = 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else { int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } vector<int> vs[MAXN]; void dfs2(int u){ for(int i=0;i<vs[u].size();i++){ dfs2(vs[u][i]); num[u]+=num[vs[u][i]]; } } void build(){ for(int i=2;i<=tot;i++) { vs[fa[i]].push_back(i); } } void work(){ build(); dfs2(1); for(int i=1;i<=tot;i++){ a[len[i]]=max(a[len[i]],num[i]); } } }sam; const int maxn=1e6+10; char ss[maxn]; int main(){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.work(); for(int i=l;i>=1;i--){ a[i]=max(a[i+1],a[i]); } for(int i=1;i<=l;i++){ printf("%d\n",a[i]); } return 0; }
多个字符串的最长公共子串长度 拓扑排序 后缀树上父子关系
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM { static const int maxn=1000010<<1;//大小为字符串长度两倍 static const int LS=27; int tot, last, ch[maxn][LS], fa[maxn], len[maxn]; int sum[maxn], tp[maxn]; int num[maxn]; int dp[maxn]; void init( void){ last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; len[np] = len[p] + 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else{ int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else{ int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void toposort( void){ for(int i = 1; i <= len[last]; i++) sum[i] = 0; for(int i = 1; i <= tot; i++) sum[len[i]]++; for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1]; for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i; } void make(){ for(int i=1;i<=tot;i++) num[i]=len[i]; toposort(); } void use(char *tt){ int l=strlen(tt); int p=1; int lll=0; for(int i=0;i<l;i++){ if(ch[p][tt[i]-'a']){ p=ch[p][tt[i]-'a']; lll++; } else { while(ch[p][tt[i]-'a']==0 && p) p=fa[p]; if(!p) { lll=0; p=1; } else { lll=len[p]+1; p=ch[p][tt[i]-'a']; } } dp[p]=max(dp[p],lll); } for(int i=tot;i>=1;i--){ int p=tp[i]; if(num[p]>dp[p]) num[p]=dp[p]; if(fa[p] && dp[fa[p]]<dp[p]) dp[fa[p]]=dp[p]; dp[p]=0; } } void work(){ int ans=0; for(int i=1;i<=tot;i++){ ans=max(ans,num[i]); } printf("%d\n",ans); } }sam; const int maxn=1e6+10; char ss[maxn]; char tt[maxn]; int main(){ scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++) sam.add(ss[i]-'a'); sam.make(); int c=0; while(++c<=100 && scanf("%s",tt)!=EOF ){ sam.use(tt); } sam.work(); }
hdu 4436 字串和
#include<bits/stdc++.h> #define ll long long using namespace std; struct SAM{ #define ll long long #define LL long long static const int maxn=1e6+10; static const int MAXN=maxn; //大小为字符串长度两倍 static const int LS=11; static const int mod=2012; int tot,last,ch[MAXN][LS],fa[MAXN],len[MAXN]; int sum[MAXN],tp[MAXN],cnt[MAXN]; // sum tp 用于拓扑排序 cnt 用于计数 int vis[maxn]; void init(){ last=tot=1; len[1] = 0; memset(ch[1],0,sizeof ch[1]); } void add( int x) { // cout<<x<<endl; int p = last, np = last = ++tot; len[np] = len[p] + 1; //cnt[last] = 1; 统计每种字符串的个数 vis[np]=(x==10); memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else{ int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else{ int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; vis[nq]=vis[q]; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void toposort(){ for(int i = 1; i <= len[last]; i++) sum[i] = 0; for(int i = 1; i <= tot; i++) sum[len[i]]++; for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1]; for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i; // for(int i = tot; i; i--) cnt[fa[tp[i]]] += cnt[tp[i]]; } int dp[maxn]; //int cnt[maxn]; void make(){ toposort(); int ans=0; cnt[1]=1; for(int i=1;i<=tot;i++){ int x=tp[i]; ans+=dp[x]%mod; ans%=mod; for(int j=0;j<LS-1;j++){ if(j==0 && i==1 ) continue; if(ch[x][j] && vis[x]==0 ){ int y=ch[x][j]; cnt[y]+=cnt[x]; cnt[y]%=mod; dp[y]+=dp[x]*10+j*cnt[x]; dp[y]%=mod; } } dp[x]=0; cnt[x]=0; } printf("%d\n",ans%mod); } }sam; const int maxn=1e6+10; char ss[maxn]; int main(){ int n; while(scanf("%d",&n)!=EOF){ sam.init(); for(int i=1;i<=n;i++){ scanf("%s",ss); int l=strlen(ss); for(int j=0;j<l;j++) sam.add(ss[j]-'0'); if(i!=n) sam.add(10); } sam.make(); } }
poj 3415 两字符串的相同字串 且长度大于K 的对数
#include<stdio.h> #include<iostream> #include<algorithm> #include<string.h> #include<string> using namespace std; struct SAM{ #define ll long long #define LL long long static const int maxn=1000010<<1;//大小为字符串长度两倍 static const int MAXN=maxn; static const int LS=54; int tot, last, ch[maxn][LS], fa[maxn], len[maxn]; int sum[MAXN], tp[MAXN], cnt[MAXN]; void init( void){ last = tot = 1; len[1] = 0; memset( ch[1], 0, sizeof ch[1]); } void add( int x) { int p = last, np = last = ++tot; cnt[last]=1; len[np] = len[p] + 1; memset( ch[np], 0, sizeof ch[np]); while( p && !ch[p][x]) ch[p][x] = np, p = fa[p]; if( p == 0) fa[np] = 1; else{ int q = ch[p][x]; if( len[q] == len[p] + 1) fa[np] = q; else { int nq = ++tot; memcpy( ch[nq], ch[q], sizeof ch[q]); len[nq] = len[p] + 1, fa[nq] = fa[q], fa[q] = fa[np] = nq; while( p && ch[p][x] == q) ch[p][x] = nq, p = fa[p]; } } } void toposort( void){ for(int i = 1; i <= len[last]; i++) sum[i] = 0; for(int i = 1; i <= tot; i++) sum[len[i]]++; for(int i = 1; i <= len[last]; i++) sum[i] += sum[i-1]; for(int i = 1; i <= tot; i++) tp[sum[len[i]]--] = i; for(int i = tot; i; i--) cnt[fa[tp[i]]] += cnt[tp[i]]; } ll ppp[maxn]; void s_init(int k){ for(int i=1;i<=tot;i++){ int x=tp[i]; if(len[x]>=k){ ppp[x]=min(len[x]-len[fa[x]],len[x]-k+1)*cnt[x]; } ppp[x]+=ppp[fa[x]]; } } void slove(char *tt,int k){ toposort(); s_init(k); int p=1,d=0; ll ans=0; int l=strlen(tt); for(int i=0;i<l;i++){ int x=0; if(tt[i]>='A' && tt[i]<='Z' ) x=tt[i]-'A'+26; else x=tt[i]-'a'; if(~ch[p][x]){ while(ch[p][x]==0 && p!=1){ p=fa[p]; d=len[p]; } } if(ch[p][x]){ p=ch[p][x]; d++; if(d>=k) { ans+=cnt[p]*(d-max(len[fa[p]],k-1)); ans+=ppp[fa[p]]; } } else { p=1; d=0; } } printf("%lld\n",ans); } void clear(){ for(int i=1;i<=tot;i++){ cnt[i]=0; tp[i]=0; ppp[i]=0; } } }sam; const int maxn=1e5+10; char ss[maxn]; char tt[maxn]; int main(){ while(1){ int k; scanf("%d",&k);if(k==0) break; scanf("%s",ss); int l=strlen(ss); sam.init(); for(int i=0;i<l;i++){ if(ss[i]>='A' && ss[i]<='Z') sam.add(ss[i]-'A'+26); else sam.add(ss[i]-'a'); } scanf("%s",tt);sam.slove(tt,k); sam.clear(); } return 0; }
后缀数组 模板
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define rank rk using namespace std; typedef long long LL; const int maxn = 1050005, M = 300; // sa[1-n] -> 0->n-1; int sa[maxn], rank[maxn], height[maxn]; int wa[maxn], wb[maxn], wv[maxn], cnt[maxn]; void SA(int *r, int n, int m) { int *x = wa, *y = wb; for(int i = 0; i < m; i++) cnt[i] = 0; for(int i = 0; i < n; i++) cnt[x[i] = r[i]]++; for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--cnt[x[i]]] = i; for(int j = 1; j < n; j <<= 1) { int p = 0; for(int i = n - j; i < n; i++) y[p++] = i; for(int i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j; for(int i = 0; i < n; i++) wv[i] = x[y[i]]; for(int i = 0; i < m; i++) cnt[i] = 0; for(int i = 0; i < n; i++) cnt[wv[i]]++; for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--cnt[wv[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]] && y[sa[i - 1] + j] == y[sa[i] + j] ? p - 1 : p++; if(p >= n) break; m = p; } } void calcHeight(int *r, int n) { int i, j, k; for(i = j = k = 0; i < n; height[rank[i++]] = k) for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++); } int n, s[maxn]; char str[maxn]; int main() { //int T; scanf("%d", &T); //while(T--) { scanf("%s", str); n = strlen(str); for(int i = 0; i < n; i++) s[i] = str[i]; s[n] = 0; SA(s, n + 1, M); for(int i = 0; i <= n; i++) rank[sa[i]] = i; calcHeight(s, n); int ans = (LL)n * (n + 1) >> 1; for(int i = 1; i <= n; i++) ans -= height[i]; //printf("%d\n", ans); for(int i=1;i<=n;i++){ if(i!=1) cout<<" "; cout<<sa[i]; }cout<<endl; for(int i=0;i<n;i++){ if(i!=0) cout<<" "; cout<<rank[i]; }cout<<endl; //} return 0; }
/* 之前一直用倍增法,发现有些题目卡倍增法,而DC3却能AC,所以顺便弄了 DC3的模版,看以后会不会用到,嗯,就是酱紫 提一些注意点:1.MAXN开n的十倍大小; 2.dc3(r,sa,n+1,Max+1);r为待后缀处理的数组,sa为存储排名位置的数组,n+1和Max+1 都和倍增一样 3.calheight(r,sa,n);和倍增一样 模版测试题目是 SPOJ 694 / SPOJ DISUBSTR Distinct Substrings【后缀数组】不相同的子串的个数 做法和我之前写的那篇题解一样,大概就这些.. */ #include<cstdio> #include<algorithm> #include<queue> #include<iostream> #include<cmath> #include<cstring> #define ll long long #define rank ra using namespace std; #define F(x) ((x)/3+((x)%3==1?0:tb)) #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2) const int MAXN = 500000+100;//n*10 int sa[MAXN]; int rank[MAXN]; int height[MAXN]; int n; char s[MAXN]; int r[MAXN]; int wa[MAXN],wb[MAXN],wv[MAXN]; int wws[MAXN]; void sort(int *r,int *a,int *b,int n,int m) { int i; for(i=0;i<n;i++) wv[i]=r[a[i]]; for(i=0;i<m;i++) wws[i]=0; for(i=0;i<n;i++) wws[wv[i]]++; for(i=1;i<m;i++) wws[i]+=wws[i-1]; for(i=n-1;i>=0;i--) b[--wws[wv[i]]]=a[i]; return; } int c0(int *r,int a,int b) {return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];} int c12(int k,int *r,int a,int b) {if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1); else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];} void dc3(int *r,int *sa,int n,int m) { int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p; r[n]=r[n+1]=0; for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i; sort(r+2,wa,wb,tbc,m); sort(r+1,wb,wa,tbc,m); sort(r,wa,wb,tbc,m); for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++) rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++; if(p<tbc) dc3(rn,san,tbc,p); else for(i=0;i<tbc;i++) san[rn[i]]=i; for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3; if(n%3==1) wb[ta++]=n-1; sort(r,wb,wa,ta,m); for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i; for(i=0,j=0,p=0;i<ta && j<tbc;p++) sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++]; for(;i<ta;p++) sa[p]=wa[i++]; for(;j<tbc;p++) sa[p]=wb[j++]; return; } void calheight(int *r, int *sa, int n) { int i, j, k = 0; for (i = 1; i <= n; ++i) rank[sa[i]] = i; for (i = 0; i < n; height[rank[i++]] = k) for (k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; ++k); return; } int main() { int t; scanf("%d",&t); while(t--){ scanf("%s",s); int Max=-1; n=strlen(s); for(int i=0;i<n;i++){ r[i]=s[i]; if(r[i]>Max)Max=r[i]; } r[n]=0; dc3(r,sa,n+1,Max+1); calheight(r,sa,n); ll sum=0; for(int i=2;i<=n;i++)sum+=height[i]; printf("%lld\n",1ll*(1+n)*n/2-sum); } return 0; } DC3版本
最小表示法
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define rank rk using namespace std; typedef long long LL; const int maxn = 30505, M = 300; // sa[1-n] -> 0->n-1; int sa[maxn], rank[maxn], height[maxn]; int wa[maxn], wb[maxn], wv[maxn], cnt[maxn]; void SA(int *r, int n, int m) { int *x = wa, *y = wb; for(int i = 0; i < m; i++) cnt[i] = 0; for(int i = 0; i < n; i++) cnt[x[i] = r[i]]++; for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--cnt[x[i]]] = i; for(int j = 1; j < n; j <<= 1) { int p = 0; for(int i = n - j; i < n; i++) y[p++] = i; for(int i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j; for(int i = 0; i < n; i++) wv[i] = x[y[i]]; for(int i = 0; i < m; i++) cnt[i] = 0; for(int i = 0; i < n; i++) cnt[wv[i]]++; for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--cnt[wv[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]] && y[sa[i - 1] + j] == y[sa[i] + j] ? p - 1 : p++; if(p >= n) break; m = p; } } void calcHeight(int *r, int n) { int i, j, k; for(i = j = k = 0; i < n; height[rank[i++]] = k) for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++); } int n, s[maxn]; char str[maxn]; int main() { int T; scanf("%d", &T); while(T--) { scanf("%s", str); n = strlen(str); for(int i = 0; i < n; i++) s[i] = str[i]; for(int i = 0; i < n; i++) s[i+n]=str[i]; n=n*2; s[n] = 0; SA(s, n + 1, M); for(int i = 0; i <= n; i++) rank[sa[i]] = i; calcHeight(s, n); int f=0; int ans=1000000; for(int i = 1; i <= n; i++) { int x=sa[i]; if(f==1){ if(x<n/2 && height[i] == n-sa[i-1] ){ ans=min(ans,x+1); } else break; continue; } if(x<n/2){ ans=x+1; f=1; } } printf("%d\n",ans); } return 0; }
不同字串个数
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define rank rk using namespace std; typedef long long LL; const int maxn = 50005, M = 300; int sa[maxn], rank[maxn], height[maxn]; int wa[maxn], wb[maxn], wv[maxn], cnt[maxn]; void SA(int *r, int n, int m) { int *x = wa, *y = wb; for(int i = 0; i < m; i++) cnt[i] = 0; for(int i = 0; i < n; i++) cnt[x[i] = r[i]]++; for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--cnt[x[i]]] = i; for(int j = 1; j < n; j <<= 1) { int p = 0; for(int i = n - j; i < n; i++) y[p++] = i; for(int i = 0; i < n; i++) if(sa[i] >= j) y[p++] = sa[i] - j; for(int i = 0; i < n; i++) wv[i] = x[y[i]]; for(int i = 0; i < m; i++) cnt[i] = 0; for(int i = 0; i < n; i++) cnt[wv[i]]++; for(int i = 1; i < m; i++) cnt[i] += cnt[i - 1]; for(int i = n - 1; i >= 0; i--) sa[--cnt[wv[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]] && y[sa[i - 1] + j] == y[sa[i] + j] ? p - 1 : p++; if(p >= n) break; m = p; } } void calcHeight(int *r, int n) { int i, j, k; for(i = j = k = 0; i < n; height[rank[i++]] = k) for(k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; k++); } int n, s[maxn]; char str[maxn]; int main() { int T; scanf("%d", &T); while(T--) { scanf("%s", str); n = strlen(str); for(int i = 0; i < n; i++) s[i] = str[i]; s[n] = 0; SA(s, n + 1, M); for(int i = 0; i <= n; i++) rank[sa[i]] = i; calcHeight(s, n); int ans = (LL)n * (n + 1) >> 1; for(int i = 1; i <= n; i++) ans -= height[i]; printf("%d\n", ans); } return 0; }
/* 之前一直用倍增法,发现有些题目卡倍增法,而DC3却能AC,所以顺便弄了 DC3的模版,看以后会不会用到,嗯,就是酱紫 提一些注意点:1.MAXN开n的十倍大小; 2.dc3(r,sa,n+1,Max+1);r为待后缀处理的数组,sa为存储排名位置的数组,n+1和Max+1 都和倍增一样 3.calheight(r,sa,n);和倍增一样 模版测试题目是 SPOJ 694 / SPOJ DISUBSTR Distinct Substrings【后缀数组】不相同的子串的个数 做法和我之前写的那篇题解一样,大概就这些.. */ #include<cstdio> #include<algorithm> #include<queue> #include<iostream> #include<cmath> #include<cstring> #define ll long long #define rank ra using namespace std; #define F(x) ((x)/3+((x)%3==1?0:tb)) #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2) const int MAXN = 500000+100;//n*10 int sa[MAXN]; int rank[MAXN]; int height[MAXN]; int n; char s[MAXN]; int r[MAXN]; int wa[MAXN],wb[MAXN],wv[MAXN]; int wws[MAXN]; void sort(int *r,int *a,int *b,int n,int m) { int i; for(i=0;i<n;i++) wv[i]=r[a[i]]; for(i=0;i<m;i++) wws[i]=0; for(i=0;i<n;i++) wws[wv[i]]++; for(i=1;i<m;i++) wws[i]+=wws[i-1]; for(i=n-1;i>=0;i--) b[--wws[wv[i]]]=a[i]; return; } int c0(int *r,int a,int b) {return r[a]==r[b]&&r[a+1]==r[b+1]&&r[a+2]==r[b+2];} int c12(int k,int *r,int a,int b) {if(k==2) return r[a]<r[b]||r[a]==r[b]&&c12(1,r,a+1,b+1); else return r[a]<r[b]||r[a]==r[b]&&wv[a+1]<wv[b+1];} void dc3(int *r,int *sa,int n,int m) { int i,j,*rn=r+n,*san=sa+n,ta=0,tb=(n+1)/3,tbc=0,p; r[n]=r[n+1]=0; for(i=0;i<n;i++) if(i%3!=0) wa[tbc++]=i; sort(r+2,wa,wb,tbc,m); sort(r+1,wb,wa,tbc,m); sort(r,wa,wb,tbc,m); for(p=1,rn[F(wb[0])]=0,i=1;i<tbc;i++) rn[F(wb[i])]=c0(r,wb[i-1],wb[i])?p-1:p++; if(p<tbc) dc3(rn,san,tbc,p); else for(i=0;i<tbc;i++) san[rn[i]]=i; for(i=0;i<tbc;i++) if(san[i]<tb) wb[ta++]=san[i]*3; if(n%3==1) wb[ta++]=n-1; sort(r,wb,wa,ta,m); for(i=0;i<tbc;i++) wv[wb[i]=G(san[i])]=i; for(i=0,j=0,p=0;i<ta && j<tbc;p++) sa[p]=c12(wb[j]%3,r,wa[i],wb[j])?wa[i++]:wb[j++]; for(;i<ta;p++) sa[p]=wa[i++]; for(;j<tbc;p++) sa[p]=wb[j++]; return; } void calheight(int *r, int *sa, int n) { int i, j, k = 0; for (i = 1; i <= n; ++i) rank[sa[i]] = i; for (i = 0; i < n; height[rank[i++]] = k) for (k ? k-- : 0, j = sa[rank[i] - 1]; r[i + k] == r[j + k]; ++k); return; } int main() { int t; scanf("%d",&t); while(t--){ scanf("%s",s); int Max=-1; n=strlen(s); for(int i=0;i<n;i++){ r[i]=s[i]; if(r[i]>Max)Max=r[i]; } r[n]=0; dc3(r,sa,n+1,Max+1); calheight(r,sa,n); ll sum=0; for(int i=2;i<=n;i++)sum+=height[i]; printf("%lld\n",1ll*(1+n)*n/2-sum); } return 0; }
KMP算法
KMP模板 (next[0]=-1) next[i] 的值是 (1~i-1) 的后缀和 (0-i-1) 的前缀 相同字串
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; char ss[maxn],tt[maxn]; int nt[maxn]; void get_next(char *tt){ int l=strlen(tt); nt[0]=-1; int j=0,k=-1; while(j<l){ if(k==-1 || tt[j]==tt[k]) nt[++j]=++k; else k=nt[k]; } } int KMP(char *ss,char *tt){ // ss 文本串 tt 匹配串 <-nt get_next(tt); int ls=strlen(ss); int lt=strlen(tt); int i=0,j=0; while(i<ls && j<lt){ if(j==-1 || ss[i]==tt[j]){ i++; j++; } else j=nt[j]; if(j==lt) return i-lt+1; } return -1; } int main(){ }
洛谷 KMP 模板 (两种写法)
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int nt[maxn]; vector<int> vs; void get_next(char *tt){ int l=strlen(tt); nt[0]=-1; int j=0,k=-1; while(j<l){ if(k==-1 || tt[j]==tt[k]) nt[++j]=++k; else k=nt[k]; } } void KMP(char *ss,char *tt){ // ss 文本串 tt 匹配串 <-nt get_next(tt); int ls=strlen(ss); int lt=strlen(tt); int i=0,j=0; while(i<ls && j<lt){ if(j==-1 || ss[i]==tt[j]){ i++; j++; } else j=nt[j]; if(j==lt) { vs.push_back(i-lt+1);j=nt[j]; } } } char ss[maxn],tt[maxn]; int main(){ cin>>ss; cin>>tt; int l=strlen(tt); KMP(ss,tt); for(int i=0;i<vs.size();i++) cout<<vs[i]<<endl; for(int i=1;i<=l;i++){ if(i!=1) cout<<" "; cout<<nt[i]; } cout<<endl; }
#include<bits/stdc++.h> using namespace std; int nt[1000005]; char tt[1000005]; char ss[1000005]; int KMP(char *ss,char *tt){ int t=strlen(tt); nt[0]=-1; for (int i = 1; i < t; ++i) { int j = nt[i-1]; while(tt[j+1] != tt[i] && j >= 0) j = nt[j]; if(tt[j+1] == tt[i]) nt[i] = j+1; else nt[i] = -1; } int s=strlen(ss); int i=0,j=0; while(i<s){ if(ss[i]==tt[j]){ i++; j++; if(j==t){ printf("%d\n",i-t+1); j=nt[j-1]+1; } } else { if(j==0)i++; else j=nt[j-1]+1; } } } int main(){ cin>>tt; cin>>ss; int ls=strlen(ss); int num=KMP(tt,ss); for(int i=0;i<ls;i++){ if(i!=0) cout<<" "; cout<<nt[i]+1; }cout<<endl; }
数组tt 在 数组ss 第一次出现的位置 (next[0]=-1);
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int ss[maxn],tt[maxn]; int nt[maxn]; int n,m; void get_next(){ int l=m; nt[0]=-1; int j=0,k=-1; while(j<l){ if(k==-1 || tt[j]==tt[k]) nt[++j]=++k; else k=nt[k]; } } int KMP(){ // ss 文本串 tt 匹配串 <-nt get_next(); int ls=n; int lt=m; int i=0,j=0; while(i<ls && j<lt){ if(j==-1 || ss[i]==tt[j]){ i++; j++; } else j=nt[j]; if(j==lt) return i-lt+1; } return -1; } int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d %d",&n,&m); for(int i=0;i<n;i++) scanf("%d",&ss[i]); for(int i=0;i<m;i++) scanf("%d",&tt[i]); printf("%d\n",KMP()); for(int i=0;i<m;i++) nt[i]=0; } }
字符串tt 在 字符串ss中出现的次数
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int nt[maxn]; void get_next(char *tt){ int l=strlen(tt); nt[0]=-1; int j=0,k=-1; while(j<l){ if(k==-1 || tt[j]==tt[k]) nt[++j]=++k; else k=nt[k]; } } int KMP(char *ss,char *tt){ // ss 文本串 tt 匹配串 <-nt get_next(tt); int ls=strlen(ss); int lt=strlen(tt); int i=0,j=0; int num=0; while(i<ls && j<lt){ if(j==-1 || ss[i]==tt[j]){ i++; j++; } else j=nt[j]; if(j==lt) { num++; j=nt[j]; } } return num; } char ss[maxn],tt[maxn]; int main(){ int T; scanf("%d",&T); while(T--){ scanf("%s",tt); scanf("%s",ss); int x=KMP(ss,tt); printf("%d\n",x); } }
字符串tt 在 字符串ss中出现的次数 (不重叠)
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; int nt[maxn]; void get_next(char *tt){ int l=strlen(tt); nt[0]=-1; int j=0,k=-1; while(j<l){ if(k==-1 || tt[j]==tt[k]) nt[++j]=++k; else k=nt[k]; } } int KMP(char *ss,char *tt){ // ss 文本串 tt 匹配串 <-nt get_next(tt); int ls=strlen(ss); int lt=strlen(tt); int i=0,j=0; int num=0; while(i<ls && j<lt){ if(j==-1 || ss[i]==tt[j]){ i++; j++; } else j=nt[j]; if(j==lt) { num++; j=0; } } return num; } char ss[maxn],tt[maxn]; int main(){ while(1){ scanf("%s",tt); if(tt[0]=='#') break; scanf("%s",ss); int x=KMP(tt,ss); printf("%d\n",x); } }
EXKMP 有两个字符串aa,bb,要求输出bb与aa的每一个后缀的最长公共前缀
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; char ss[maxn],tt[maxn]; int p[maxn], ex[maxn]; void EXKMP(char *ss,char *tt){ // ss 文本串 tt匹配串 int ls=strlen(ss+1),lt=strlen(tt+1); p[1]=lt; int j=1; while(tt[j]==tt[j+1] && j+1<=lt) j++; p[2]=j-1; // 2-n 1-n 的最长公共前缀; int k=2; // 最长匹配的下标 for(int i=3;i<=lt;i++){ int pp=k+p[k]-1; // int L=p[i-k+1]; // if(i-k+L<pp-k+1){ p[i]=L; } else if(i-k+L>=pp-k+1){ int j=pp-i+1; if(j<0) j=0; while(tt[j+1] == tt[i+j] && i+j<=lt) j++; p[i]=j; k=i; } } j=1; while(ss[j]==tt[j] && j<=ls && j<=lt) j++; ex[1]=j-1; k=1; for(int i=2;i<=ls;i++){ int pp=ex[k]+k-1; int L=p[i-k+1]; if(i-k+L<pp-k+1){ ex[i]=L; } else if(i-k+L>=pp-k+1){ int j=pp-i+1; if(j<0) j=0; while(tt[j+1]==ss[i+j] && i+j<=ls && j+1<=lt ) j++; ex[i]=j; k=i; } } } int main(){ scanf("%s",ss+1); scanf("%s",tt+1); int ls=strlen(ss+1); int lt=strlen(tt+1); EXKMP(ss,tt); for(int i=1;i<=lt;i++){ if(i!=1) cout<<" "; cout<<p[i]; }cout<<endl; for(int i=1;i<=ls;i++){ if(i!=1) cout<<" "; cout<<ex[i];}cout<<endl; }
hd2594 找aa的最长前缀是bb的后缀
#include<bits/stdc++.h> using namespace std; const int maxn=1e6+10; char ss[maxn],tt[maxn]; int p[maxn], ex[maxn]; void EXKMP(char *ss,char *tt){ // ss 文本串 tt匹配串 int ls=strlen(ss+1),lt=strlen(tt+1); p[1]=lt; int j=1; while(tt[j]==tt[j+1] && j+1<=lt) j++; p[2]=j-1; // 2-n 1-n 的最长公共前缀; int k=2; // 最长匹配的下标 for(int i=3;i<=lt;i++){ int pp=k+p[k]-1; // int L=p[i-k+1]; // if(i-k+L<pp-k+1){ p[i]=L; } else if(i-k+L>=pp-k+1){ int j=pp-i+1; if(j<0) j=0; while(tt[j+1] == tt[i+j] && i+j<=lt) j++; p[i]=j; k=i; } } j=1; while(ss[j]==tt[j] && j<=ls && j<=lt) j++; ex[1]=j-1; k=1; for(int i=2;i<=ls;i++){ int pp=ex[k]+k-1; int L=p[i-k+1]; if(i-k+L<pp-k+1){ ex[i]=L; } else if(i-k+L>=pp-k+1){ int j=pp-i+1; if(j<0) j=0; while(tt[j+1]==ss[i+j] && i+j<=ls && j+1<=lt ) j++; ex[i]=j; k=i; } } } int main(){ while(scanf("%s",tt+1)!=EOF){ scanf("%s",ss+1); int ls=strlen(ss+1); int lt=strlen(tt+1); EXKMP(ss,tt); int ans=0; for(int i=1;i<=ls;i++){ if(i+ex[i]-1==ls) ans=max(ans,ex[i]); } if(ans==0){ printf("0\n"); } else { for(int i=1;i<=ls;i++){ if(ex[i]==ans && i+ex[i]-1==ls){ for(int j=i;j<i+ex[i];j++){ printf("%c",ss[j]); } break; } } printf(" %d\n",ans); } } }
AC自动机 模板 (初始化 插入 构建 查询 清空)
#include<bits/stdc++.h> using namespace std; struct AC{ static const int maxn=1e6+10; static const int MAXN=maxn; static const int ls=26; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn],val[maxn]; int tot=0; queue<int> q; void init(){ tot=0; } void Insert(char *ss){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x=ss[i]-'a'; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } val[now]++; // 统计字符出现的次数 } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } int Find(char *ss){ int l=strlen(ss); int now=0; int ans=0; for(int i=0;i<l;i++){ int x=ss[i]-'a'; now=ch[now][x]; for(int j=now;j&&val[j];j=fail[j]){ ans+=val[j]; val[j]=0; } } return ans; } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; for(int j=0;j<ls;j++) ch[i][j]=0; } } }ac; const int maxn=1e6+10; char ss[maxn]; int main(){ }
洛谷 P3808 模式串在 文本串中出现的个数
#include<bits/stdc++.h> using namespace std; struct AC{ static const int maxn=1e6+10; static const int MAXN=maxn; static const int ls=26; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn],val[maxn]; int tot=0; queue<int> q; void Insert(char *ss){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x=ss[i]-'a'; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } val[now]++; // 统计字符出现的次数 } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } int Find(char *ss){ int l=strlen(ss); int now=0; int ans=0; for(int i=0;i<l;i++){ int x=ss[i]-'a'; now=ch[now][x]; for(int j=now;j&&val[j];j=fail[j]){ ans+=val[j]; val[j]=0; } } return ans; } }ac; const int maxn=1e6+10; char ss[maxn]; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",ss); ac.Insert(ss); } ac.fail_tree(); scanf("%s",ss); printf("%d\n",ac.Find(ss)); }
洛谷 P3496 输出 出现次数最多的模式串 AC 自动机 fail 指针的拓扑排序 不是 1-tot 同后缀自动机
#include<bits/stdc++.h> using namespace std; struct AC{ static const int maxn=1e6+10; static const int MAXN=maxn; static const int ls=26; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn],val[maxn]; int tot=0; queue<int> q; void init(){ tot=0; } int Insert(char *ss){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x=ss[i]-'a'; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } return now; } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } void Find(char *ss){ int l=strlen(ss); int now=0; int ans=0; for(int i=0;i<l;i++){ int x=ss[i]-'a'; now=ch[now][x]; val[now]++; } } vector<int> vs[maxn]; void dfs(int x){ for(int i=0;i<vs[x].size();i++){ dfs(vs[x][i]); val[x]+=val[vs[x][i]]; } } void make(){ for(int i=1;i<=tot;i++) vs[fail[i]].push_back(i); dfs(0); } void work(){ make(); //for(int i=tot;i>=1;i--){ val[fail[i]]+=val[i]; } } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; vs[i].clear(); for(int j=0;j<ls;j++) ch[i][j]=0; } } }ac; const int maxn=1e6+10; char ss[160][210]; char tt[maxn]; int pos[maxn]; int main(){ while(1){ int n; scanf("%d",&n); if(n==0) break; ac.init(); for(int i=1;i<=n;i++){ scanf("%s",ss[i]); pos[i]=ac.Insert(ss[i]); }ac.fail_tree(); scanf("%s",tt); ac.Find(tt); ac.work(); int temp=0; for(int i=1;i<=n;i++){ temp=max(temp,ac.val[pos[i]]); } printf("%d\n",temp); for(int i=1;i<=n;i++){ if(ac.val[pos[i]]==temp) printf("%s\n",ss[i]); } ac.clear(); } }
洛谷 P5357 模式串出现次数
#include<bits/stdc++.h> using namespace std; struct AC{ static const int maxn=2e6+10; static const int MAXN=maxn; static const int ls=26; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn],val[maxn]; int tot=0; queue<int> q; void init(){ tot=0; } int Insert(char *ss){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x=ss[i]-'a'; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } return now; } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } void Find(char *ss){ int l=strlen(ss); int now=0; int ans=0; for(int i=0;i<l;i++){ int x=ss[i]-'a'; now=ch[now][x]; val[now]++; } } vector<int> vs[maxn]; void dfs(int x){ for(int i=0;i<vs[x].size();i++){ dfs(vs[x][i]); val[x]+=val[vs[x][i]]; } } void make(){ for(int i=1;i<=tot;i++) vs[fail[i]].push_back(i); dfs(0); } void work(){ make(); //for(int i=tot;i>=1;i--){ val[fail[i]]+=val[i]; } } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; vs[i].clear(); for(int j=0;j<ls;j++) ch[i][j]=0; } } }ac; const int maxn=2e6+10; char ss[maxn]; char tt[maxn]; int pos[maxn]; int main(){ int n; scanf("%d",&n); ac.init(); for(int i=1;i<=n;i++){ scanf("%s",ss); pos[i]=ac.Insert(ss); }ac.fail_tree(); scanf("%s",tt); ac.Find(tt); ac.work(); int temp=0; for(int i=1;i<=n;i++){ temp=ac.val[pos[i]]; printf("%d\n",temp); } }
hdu 2896 多模式串 多匹配串
#include<bits/stdc++.h> using namespace std; vector<pair<int,int> > vs; struct AC{ static const int maxn=1e5+10; static const int MAXN=maxn; static const int ls=128; #define ll long long #define LL long long int fail[maxn],val[maxn]; int ch[maxn][ls]; int tot=0; queue<int> q; void init(){ tot=0; } void Insert(char *ss,int k){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x=ss[i]; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } val[now]=k; // 统计字符出现的次数 } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){ fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } void Find(char *ss){ int l=strlen(ss); int now=0; int ans=0; for(int i=0;i<l;i++){ int x=ss[i]; now=ch[now][x]; for(int j=now;j&&val[j];j=fail[j]){ if(val[j]) vs.push_back({val[j],j}); val[j]=0; } } } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; for(int j=0;j<ls;j++) ch[i][j]=0; } } }ac; const int maxn=1e6+10; char ss[maxn]; int main(){ int n; scanf("%d",&n); ac.init(); int k=0; for(int i=1;i<=n;i++){ scanf("%s",ss); ac.Insert(ss,i); } ac.fail_tree(); int m; scanf("%d",&m); for(int i=1;i<=m;i++){ scanf("%s",ss); ac.Find(ss); sort(vs.begin(),vs.end()); if(vs.size()==0) continue; printf("web %d:",i); for(int i=0;i<vs.size();i++){ printf(" %d",vs[i].first); ac.val[vs[i].second]=vs[i].first; } printf("\n"); if(vs.size()) k++; vs.clear(); } printf("total: %d\n",k); }
poj 2778 AC自动机 + 矩阵快速幂
#include<stdio.h> #include<string> #include<cstring> #include<vector> #include<queue> #define ll long long #define LL long long using namespace std; vector<int> vs; const int mod=100000; const int MOD=mod; struct Matrix { ll a[105][105]; void init() { for (int i = 0; i < 105; i++) { for (int j = 0; j < 105; j++) a[i][j] = 0; } } void _init() { init(); for (int i = 0; i < 105; i++) a[i][i] = 1; } }A, B; Matrix mul(Matrix a, Matrix b) { Matrix ans; ans.init(); for (int i = 0; i < 105; i++) { for (int j = 0; j < 105; j++) { if(a.a[i][j]) { for (int k = 0; k < 105; k++) ans.a[i][k] = (ans.a[i][k] + 1LL * a.a[i][j] * b.a[j][k]) % MOD; } } } return ans; } void make(Matrix &A){ for(int i=0;i<vs.size();i++){ int x=vs[i]; for(int j=0;j<105;j++){ A.a[x][j]=0; A.a[j][x]=0; } } } Matrix q_pow(Matrix a, int k) { Matrix ans; ans._init(); if(k <= 0) return ans; while(k) { if(k&1) ans = mul(ans, a); a = mul(a, a); k >>= 1; } return ans; } struct AC{ static const int maxn=1e6+10; static const int MAXN=maxn; static const int ls=4; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn], val[maxn]; int tot; queue<int> q; void init(){ tot=0;} void Insert(char *ss){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x; if(ss[i]=='A') x=0; else if(ss[i]=='T') x=1; else if(ss[i]=='G') x=2; else x=3; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } val[now]++; // 统计字符出现的次数 vs.push_back(now); } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); if(val[fail[x]]){ vs.push_back(x); val[x]++; } for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else { ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } } void work(int x){ A.init(); B.init(); A.a[0][0]=1; // for(int i=0;i<=tot;i++){ for(int j=0;j<ls;j++){ int y=ch[i][j]; B.a[i][y]++; } } make(B); A=mul(A,q_pow(B,x)); ll ans=0; for(int i=0;i<105;i++){ for(int j=0;j<105;j++){ ans+=A.a[i][j]; } } ans%=mod; printf("%lld\n",ans); } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; for(int j=0;j<ls;j++) ch[i][j]=0; } } }ac; const int maxn=1e6+10; char ss[maxn]; int main(){ int n,m; scanf("%d %d",&n,&m); ac.init(); for(int i=1;i<=n;i++){ scanf("%s",ss); ac.Insert(ss); } ac.fail_tree(); ac.work(m); ac.clear(); } /* 4 3 AT AC AG AA */
hdu 2243 AC自动机 + 等比数列(矩阵优化) + 矩阵等比 (矩阵优化)
#include<bits/stdc++.h> #define ll long long #define LL long long using namespace std; vector<int> vs; struct Matrix { unsigned long long a[105][105]; void init() { for (int i = 0; i < 105; i++) { for (int j = 0; j < 105; j++) a[i][j] = 0; } } void _init() { init(); for (int i = 0; i < 105; i++) a[i][i] = 1; } }A, B,C,D; Matrix mul(Matrix a, Matrix b) { Matrix ans; ans.init(); for (int i = 0; i < 105; i++) { for (int j = 0; j < 105; j++) { if(a.a[i][j]) { for (int k = 0; k < 105; k++) ans.a[i][k] = (ans.a[i][k] + a.a[i][j] * b.a[j][k]) ; } } } return ans; } void make(Matrix &A){ for(int i=0;i<vs.size();i++){ int x=vs[i]; for(int j=0;j<105;j++){ A.a[x][j]=0; A.a[j][x]=0; } } } Matrix q_pow(Matrix a, int k) { Matrix ans; ans._init(); if(k <= 0) return ans; while(k) { if(k&1) ans = mul(ans, a); a = mul(a, a); k >>= 1; } return ans; } /*unsigned int quick(unsigned int x,ll n){ unsigned int ans=1; while(n){ if(n&1) ans=ans*x; x=x*x; n=n/2; } cout<<ans<<endl; return ans; }*/ struct AC{ static const int maxn=1e6+10; static const int MAXN=maxn; static const int ls=26; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn], val[maxn]; int tot; queue<int> q; void init(){ tot=0;} void Insert(char *ss){ // 构建字典树 int now=0; int l=strlen(ss); for(int i=0;i<l;i++){ int x=ss[i]-'a'; if(!ch[now][x]) ch[now][x]=++tot; now=ch[now][x]; } val[now]++; // 统计字符出现的次数 vs.push_back(now); } void fail_tree(){ // 字典树上fail指针 (链上后缀 前缀比较) for(int i=0;i<ls;i++){ if(ch[0][i]){fail[ch[0][i]]=0; q.push(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); if(val[fail[x]]){ vs.push_back(x); val[x]++; } for(int i=0;i<ls;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); } else { ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } } void work(int x){ A.init(); B.init(); C.init(); D.init(); A.a[0][0]=1; for(int i=0;i<=tot;i++){ for(int j=0;j<ls;j++){ int y=ch[i][j]; B.a[i][y]++; } } make(B); for(int i=0;i<=tot;i++){ for(int j=0;j<=tot;j++){ C.a[i][j]=B.a[i][j]; //B if(i==j) C.a[i][j+tot+1]=1; // E else C.a[i][j+tot+1]=0; } } for(int i=0;i<=tot;i++){ for(int j=0;j<=tot;j++){ if(i==j) C.a[i+tot+1][j+tot+1]=1; // E else C.a[i+tot+1][j+tot+1]=0; C.a[i+tot+1][j]=0; } } D.a[0][0]=26; D.a[0][1]=1; D.a[1][0]=0; D.a[1][1]=1; D=q_pow(D,x); C=q_pow(C,x); B.init(); for(int i=0;i<=tot;i++){ for(int j=0;j<=tot;j++){ B.a[i][j]=C.a[i][j]+C.a[i][j+tot+1]+C.a[i+tot+1][j]-C.a[i+tot+1][j+tot+1]; } } A=mul(A,B); unsigned long long ans=D.a[0][0]+D.a[0][1]+D.a[1][0]-D.a[1][1]; for(int i=0;i<=tot;i++){ for(int j=0;j<=tot;j++){ ans-=A.a[i][j]; } } printf("%llu\n",ans); } void clear(){ for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; for(int j=0;j<ls;j++) ch[i][j]=0; } vs.clear(); } }ac; const int maxn=1e6+10; char ss[maxn]; int main(){ int n,m; while(~scanf("%d %d",&n,&m)){ ac.init(); for(int i=1;i<=n;i++){ scanf("%s",ss); ac.Insert(ss); } ac.fail_tree(); ac.work(m); ac.clear(); } return 0; } /* 3 AT AC AG AA */
hdu 4511 AC自动机 + dp + 最短路 // 可以优化个n 分层跑
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1e6+10; pair<double,double> pa[maxn]; vector<int> vs; double dis(int x,int y){ return sqrt( (pa[x].first-pa[y].first)*(pa[x].first-pa[y].first) +(pa[x].second-pa[y].second)*(pa[x].second-pa[y].second) ); } struct AC{ static const int maxn=2000+10; static const int MAXN=maxn; static const int ls=51; #define ll long long #define LL long long int ch[maxn][ls],fail[maxn],val[maxn]; double dp[maxn][ls]; int mp[maxn]; int tot=0; queue<int> q; vector<int> p; void init(){ tot=0; mp[0]=0; // --> point for(int i=0;i<maxn;i++){ for(int j=0;j<ls;j++){ dp[i][j]=1e18; } } } void Insert(vector<int> vs){ // 构建字典树 int now=0; int l=vs.size(); for(int i=0;i<l;i++){ int x=vs[i]; if(!ch[now][x]) { ch[now][x]=++tot; mp[tot]=x;}// ->point } now=ch[now][x]; } val[now]++; // 统计字符出现的次数 } void fail_tree(int n){ // 字典树上fail指针 (链上后缀 前缀比较) p.push_back(0); // bfs 序列 for(int i=0;i<n;i++){ if(ch[0][i]){ fail[ch[0][i]]=0; q.push(ch[0][i]); p.push_back(ch[0][i]); } } while(!q.empty()){ int x=q.front(); q.pop(); if(val[fail[x]] ) { val[x]++; } // limit for(int i=0;i<n;i++){ if(ch[x][i]){ fail[ch[x][i]]=ch[fail[x]][i]; q.push(ch[x][i]); p.push_back(ch[x][i]); } else ch[x][i]=ch[fail[x]][i]; // 匹配失败 转 next } } } void slove(int n){ int T=1; if(ch[0][0]) { dp[ch[0][0]][0]=0; } else { dp[0][0]=0; } // cout<<p.size()<<endl; while(T--){ for(int i=0;i<p.size();i++){ int x=p[i]; if(val[x]) continue; // cout<<x<<"==="<<mp[x]<<endl; if(x==0){ for(int j=0;j<n;j++){ for(int k=j+1;k<n;k++){ int pos=ch[x][k]; if(val[pos]) continue; dp[pos][k]=min(dp[pos][k],dp[x][j]+dis(j,k)); // cout<<pos<<" "<<k<<" "<<" "<<j<<" "<<dp[pos][k]<<endl; } } } else { for(int j=mp[x]+1;j<n;j++){ int pos=ch[x][j]; //cout<<pos<<endl; if(val[pos]) continue; dp[pos][j]=min(dp[pos][j],dp[x][mp[x]]+dis(mp[x],j)); // cout<<pos<<" "<<j<<" "<<dp[pos][j]<<endl; } } } // break; } double ans=1e18; for(int i=0;i<=tot;i++){ if(val[i]) continue; ans=min(dp[i][n-1],ans); } if(ans>1e17) printf("Can not be reached!\n"); else printf("%.2f\n",ans); } void clear(){ p.clear(); for(int i=0;i<=tot;i++){ fail[i]=0; val[i]=0; for(int j=0;j<ls;j++) ch[i][j]=0; } } }ac; int main(){ int n,m; while(~scanf("%d %d",&n,&m)){ if(n==0 && m==0 ) break; for(int i=0;i<n;i++){scanf("%lf %lf",&pa[i].first,&pa[i].second); } ac.init(); while(m--){ int k; scanf("%d",&k); vs.clear(); for(int j=1;j<=k;j++) { int x; scanf("%d",&x); x--; vs.push_back(x); } ac.Insert(vs); } ac.fail_tree(n); ac.slove(n); ac.clear(); } return 0; }
字符串匹配 shift-and
hdu 5972
#include<bits/stdc++.h> using namespace std; const int maxn=5e6+10; char ss[maxn]; bitset<1005> S[10],X; int main(){ int n; while( ~scanf("%d",&n) ){ //scanf("%d",&n); for(int i=0;i<=9;i++) S[i].reset(); X.reset(); for(int i=0;i<n;i++){ int k; scanf("%d",&k); while(k--){ int x; scanf("%d",&x); S[x].set(i); } } scanf("%s",ss); int l=strlen(ss); for(int i=0;i<l;i++){ X=(X<<1); X.set(0); X=X&S[ss[i]-'0']; if(X[n-1]){ char ttmp = ss[i+1]; ss[i+1] = '\0'; puts(ss+i+1-n); ss[i+1] = ttmp; } } } return 0; }
最小表示法模板
string s; int Min(string s) { int i = 0, j = 1, k = 0, n = s.size(), d; while(i < n && j < n && k < n) { d = s[(i+k)%n] - s[(j+k)%n]; if(!d) k++; else { if(d > 0) i += k+1; else j += k+1; if(i == j) j++; k = 0; } } return min(i, j); }