hihocoder 1403:
求一个字符串中至少出现了K次的最长可重叠子串
做法:预处理出height,每次用单调队列线性求区间最小值即可
#include<bits/stdc++.h> #define rep(ii,a,b) for(int ii=a;ii<=b;++ii) #define per(ii,a,b) for(int ii=b;ii>=a;--ii) using namespace std;//head const int maxn=4e4+10,maxm=2e6+10; int casn,n,m,k; char a[maxn]; class suffix{public: int sa[maxn],rank[maxn],h[maxn],wa[maxn],wb[maxn],wc[maxn],wd[maxn]; char *str; void geth(int n){ int i,j,k=0; for(i=1;i<=n;++i) rank[sa[i]]=i; for(i=0;i<n;h[rank[i++]]=k) for(k?k--:0,j=sa[rank[i]-1];str[i+k]==str[j+k];++k); } void getsa(char *_s,int n,int m){ str=_s; int i,j,p,*x=wa,*y=wb; rep(i,0,m-1) wd[i]=0; rep(i,0,n-1) wd[x[i]=str[i]]++; rep(i,1,m-1) wd[i]+=wd[i-1]; per(i,0,n-1) sa[--wd[x[i]]]=i; for(j=1,p=1;p<n;j*=2,m=p){ for(p=0,i=n-j;i<n;++i) y[p++]=i; rep(i,0,n-1) if(sa[i]>=j) y[p++]=sa[i]-j; rep(i,0,n-1) wc[i]=x[y[i]]; rep(i,0,m-1) wd[i]=0; rep(i,0,n-1) wd[wc[i]]++; rep(i,1,m-1) wd[i]+=wd[i-1]; per(i,0,n-1) sa[--wd[wc[i]]]=y[i]; for(swap(x,y),p=1,x[sa[0]]=0,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++; } geth(n-1); } int solve(int n,int k){ deque<int>que; int ans=0; rep(i,1,n){ while(!que.empty()&&h[i]<h[que.back()]) que.pop_back(); que.push_back(i); while(!que.empty()&&i-que.front()>=k) que.pop_front(); assert(!que.empty()); if(!que.empty())ans=max(ans,h[que.front()]); } return ans; } }suf; int main() { cin>>n>>k; rep(i,0,n-1) { int x;cin>>x; a[i]=x; } a[n]=0; k--; suf.getsa(a,n+1,233); cout<<suf.solve(n,k); return 0; }
hihocoder1407:
求字符串中最长的至少不重叠地出现了2次的子串
做法:预处理出height,二分答案,每次检查每一个连续的height<mid的区间内,最大和最小的后缀距离是否>=mid即可(我换了板子)
#include<bits/stdc++.h> #define rep(ii,a,b) for(int ii=a;ii<=b;++ii) #define per(ii,a,b) for(int ii=b;ii>=a;--ii) using namespace std;//head const int maxn=100000+10,maxm=2e6+10; int casn,n,m,k; int a[maxn]; namespace suffix{ int tr[maxn],rank[maxn],sa[maxn],h[maxn]; int has[maxn],bir[maxn],val[maxn],deep[maxn]; int cmp(int x,int y,int k){ if(x+k>n||y+k>n)return 0; return rank[x]==rank[y]&&rank[x+k]==rank[y+k]; } void getsa(int *s,int n,int m=maxn-5){ int i,cnt; for(i=1;i<=n;i++)has[s[i]]++; for(i=1,cnt=0;i<=m;i++)if(has[i])tr[i]=++cnt; for(i=1;i<=m;i++)has[i]+=has[i-1]; for(i=1;i<=n;i++)rank[i]=tr[s[i]],sa[has[s[i]]--]=i; for(int k=1;cnt!=n;k<<=1){ for(i=1;i<=n;i++)has[i]=0; for(i=1;i<=n;i++)has[rank[i]]++; for(i=1;i<=n;i++)has[i]+=has[i-1]; for(i=n;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--; for(i=1;i<=k;i++)tr[n-i+1]=has[rank[n-i+1]]--; for(i=1;i<=n;i++)sa[tr[i]]=i; for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt; for(i=1;i<=n;i++)rank[i]=tr[i]; } for(int i=1;i<=n;i++){ if(rank[i]==1)continue; for(int j=max(1,h[rank[i-1]]-1);;j++){ if(s[i+j-1]==s[sa[rank[i]-1]+j-1])h[rank[i]]=j; else break; } } } bool check(int mid,int n){ int mx=0,mn=n; rep(i,1,n){ if(h[i]<mid){ if(mx&&mx-mn>=mid) return true; mx=mn=sa[i]; }else mx=max(mx,sa[i]),mn=min(mn,sa[i]); } if(mx&&mx-mn>=mid) return true; return false; } } int main() { cin>>n; rep(i,1,n) cin>>a[i]; suffix::getsa(a,n,1010); int l=0,r=n; int ans=0; while(l<=r){ int mid=(l+r)>>1; if(suffix::check(mid,n)) ans=mid,l=mid+1; else r=mid-1; } cout<<ans; return 0; }
hihocoder 1415
求两个子串的最长公共子串
做法:拼接两个子串,中间隔着一个从没出现过的字符(我选择的是比所有字符都大的一个字符),
然后计算出height数组,接着对于每一对相邻的后缀,如果分别属于两个字符串,那么ans和他们两个的lcp取一下最大值就行
#include<bits/stdc++.h> #define rep(ii,a,b) for(int ii=a;ii<=b;++ii) #define per(ii,a,b) for(int ii=b;ii>=a;--ii) using namespace std;//head const int maxn=100000+10,maxm=2e6+10; int casn,k; namespace suffix{ int tr[maxn],rank[maxn],sa[maxn],h[maxn]; int has[maxn],bir[maxn],val[maxn],deep[maxn]; int n; int cmp(int x,int y,int k){ if(x+k>n||y+k>n)return 0; return rank[x]==rank[y]&&rank[x+k]==rank[y+k]; } void getsa(int *s,int _n,int m=maxn-5){ int i,cnt;n=_n; for(i=1;i<=n;i++)has[s[i]]++; for(i=1,cnt=0;i<=m;i++)if(has[i])tr[i]=++cnt; for(i=1;i<=m;i++)has[i]+=has[i-1]; for(i=1;i<=n;i++)rank[i]=tr[s[i]],sa[has[s[i]]--]=i; for(int k=1;cnt!=n;k<<=1){ for(i=1;i<=n;i++)has[i]=0; for(i=1;i<=n;i++)has[rank[i]]++; for(i=1;i<=n;i++)has[i]+=has[i-1]; for(i=n;i>=1;i--)if(sa[i]>k)tr[sa[i]-k]=has[rank[sa[i]-k]]--; for(i=1;i<=k;i++)tr[n-i+1]=has[rank[n-i+1]]--; for(i=1;i<=n;i++)sa[tr[i]]=i; for(i=1,cnt=0;i<=n;i++)tr[sa[i]]=cmp(sa[i],sa[i-1],k) ? cnt:++cnt; for(i=1;i<=n;i++)rank[i]=tr[i]; } for(int i=1;i<=n;i++){ if(rank[i]==1)continue; for(int j=max(1,h[rank[i-1]]-1);;j++){ if(s[i+j-1]==s[sa[rank[i]-1]+j-1])h[rank[i]]=j; else break; } } } int solve(int len,int n){ int ans=0; rep(i,1,len) if((sa[i]<=n)^(sa[i-1]<=n)) ans=max(ans,h[i]); return ans; } } char a[maxn],b[maxn]; int c[maxn<<1]; int main() { cin>>(a+1)>>(b+1); int n=strlen(a+1),m=strlen(b+1); rep(i,1,n) c[i]=a[i];c[n+1]='z'+1; rep(i,1,m) c[i+n+1]=b[i]; int len=n+m+1; suffix::getsa(c,len,233); cout<<suffix::solve(len,n); return 0; }