BZOJ3238: [Ahoi2013]差异
题解: 难点是如何求任意两个后缀的lcp 我们考虑到任意两个位置的后缀LCP取决于两者在SA中的最小值 因此我们有两种方法解决 首先对于sa数组按照h数组分治 统计每个位置的贡献 其次我们也可以单调栈找到每个位置前面第一个比他小的 和 后面第一个比他小的然后统计价值即可
分治版本:
/************************************************************** Problem: 3238 User: wang9897 Language: C++ Result: Accepted Time:2968 ms Memory:19360 kb ****************************************************************/ #include <bits/stdc++.h> #define N 500005 #define INF 100000007 #define ll long long using namespace std; int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return f*x; } char s[N];int sa[N],txt[N],rank1[N],t1[N],t2[N],rank2[N],td[N]; bool cmp(int f[],int t,int w,int k){ return f[t]==f[w]&&f[t+k]==f[w+k]; } void Sa(char str[]){ int len=strlen(str); int m=127; int *rank1=t2;int *td=t1; for(int i=0;i<m;i++) txt[i]=0; for(int i=0;i<len;i++){ rank1[i]=str[i];txt[str[i]]++; } for(int i=1;i<m;i++) txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--) sa[--txt[str[i]]]=i; for(int k=1;k<=len;k*=2){ int p=0; for(int i=len-k;i<len;i++) td[p++]=i; for(int i=0;i<len;i++){ if(sa[i]>=k) td[p++]=sa[i]-k; } for(int i=0;i<m;i++) txt[i]=0; for(int i=0;i<len;i++) txt[rank1[i]]++; for(int i=1;i<m;i++) txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--) sa[--txt[rank1[td[i]]]]=td[i]; swap(td,rank1); rank1[sa[0]]=0; p=1; for(int i=1;i<len;i++) rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++; if(p==len) return ; m=p; } } int hh[N],height[N]; void h(char str[]){ int len=strlen(str); for(int i=0;i<len;i++) rank2[sa[i]]=i; memset(hh,0,sizeof(hh)); for(int i=0;i<len;i++){ if(rank2[i]==0) continue; int t=sa[rank2[i]-1];int w=i;int k; if(i==0||hh[i-1]<=1) k=0; else{ k=hh[i-1]-1;t+=k;w+=k; } while(t<len&&w<len){ if(str[t]==str[w]) k++; else break; t++;w++; } hh[i]=k;height[rank2[i]]=k; } return ; } ll ans,ans1,ans2; void fenzhi(int l,int r){ if(l>r) return ; // cout<<l<<" "<<r<<endl; ll t=INF;int tt; if(l==r){ ans+=height[l];return ; } for(int i=l;i<=r;i++){ if(height[i]<t){ t=height[i];tt=i; } } ans+=1ll*(r-tt+1)*(tt-l+1)*t; fenzhi(l,tt-1); fenzhi(tt+1,r); } int main(){ ios::sync_with_stdio(false); scanf(" %s",&s);int len=strlen(s); s[len]='$'; Sa(s);h(s); ans=0;ans1=0;ans2=0; fenzhi(1,len); ans*=2; for(int i=len;i>=2;i--) ans1+=1ll*i*(i-1); for(int i=len-1;i>=1;i--) ans2+=1ll*(i+1)*i/2; printf("%lld\n",ans1+ans2-ans); return 0; }
单调栈:
/************************************************************** Problem: 3238 User: wang9897 Language: C++ Result: Accepted Time:5888 ms Memory:73216 kb ****************************************************************/ #include <bits/stdc++.h> #define ll long long const int MAXN=5e5+10; using namespace std; int txt[MAXN],t1[MAXN],t2[MAXN],rank1[MAXN],rank2[MAXN],td[MAXN],sa[MAXN]; bool cmp(int f[],int t,int w,int k){return f[t]==f[w]&&f[t+k]==f[k+w];} void Sa(char str[]){ int len=strlen(str);int m=250; int *td=t1;int *rank1=t2; for(int i=0;i<m;i++)txt[i]=0; for(int i=0;i<len;i++)rank1[i]=str[i],txt[str[i]]++; for(int i=1;i<m;i++)txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--)sa[--txt[str[i]]]=i; for(int k=1;k<=len;k*=2){ int p=0; for(int i=len-k;i<len;i++)td[p++]=i; for(int i=0;i<len;i++)if(sa[i]>=k)td[p++]=sa[i]-k; for(int i=0;i<m;i++)txt[i]=0; for(int i=0;i<len;i++)txt[rank1[i]]++; for(int i=1;i<m;i++)txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--)sa[--txt[rank1[td[i]]]]=td[i]; swap(rank1,td);rank1[sa[0]]=0;p=1; for(int i=1;i<len;i++)rank1[sa[i]]=cmp(td,sa[i],sa[i-1],k)?p-1:p++; if(p==len)return ; m=p; } } int h[MAXN],H[MAXN]; void hh(char str[]){ int len=strlen(str); memset(h,0,sizeof(h));memset(H,0,sizeof(H)); for(int i=0;i<len;i++)rank2[sa[i]]=i; for(int i=0;i<len;i++){ if(!rank2[i])continue; int t=sa[rank2[i]-1];int w=i;int k=0; if(!i||H[i-1]<=1)k=0; else k=H[i-1]-1,t+=k,w+=k; while(t<len&&w<len){ if(str[t]==str[w])k++; else break; t++;w++; } H[i]=k;h[rank2[i]]=k; } } int dp[MAXN][21],ma[MAXN]; void St(char str[]){ int len=strlen(str); ma[0]=-1; for(int i=1;i<MAXN;i++)if((i&(i-1))==0)ma[i]=ma[i-1]+1;else ma[i]=ma[i-1]; for(int i=1;i<len;i++)dp[i][0]=h[i]; for(int j=1;(1<<(j-1))<=len;j++){ for(int i=1;i+(1<<j)<=len;i++){ dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]); } } } int rmq(int l,int r){ if(l>=r)return 0; l++; int k=ma[r-l+1];int k1=(1<<k); return min(dp[l][k],dp[r-k1+1][k]); } char s[MAXN]; pair<int,int> st[MAXN];int tot;int rnum[MAXN],lnum[MAXN]; vector<int>vec; int main(){ scanf("%s",s);int len=strlen(s);s[len++]='$'; Sa(s);hh(s);St(s); // for(int i=1;i<len;i++)cout<<sa[i]<<" "; // cout<<endl; for(int i=2;i<len;i++)vec.push_back(rmq(i-1,i)); int sz=vec.size(); // for(int i=0;i<sz;i++)cout<<vec[i]<<" "; // cout<<endl; for(int i=0;i<sz;i++){ while(tot>0&&st[tot].first>vec[i]){tot--;} if(!tot)lnum[i]=i+1; else lnum[i]=i-st[tot].second; // cout<<lnum[i]<<"::::"; st[++tot]=make_pair(vec[i],i); } // cout<<endl; reverse(vec.begin(),vec.end());tot=0; for(int i=0;i<sz;i++){ while(tot>0&&st[tot].first>=vec[i]){tot--;} if(!tot)rnum[sz-i-1]=i+1; else rnum[sz-i-1]=i-st[tot].second; st[++tot]=make_pair(vec[i],i); } reverse(vec.begin(),vec.end()); int n=len-1; ll ans=1ll*(n-1)*(n+1)*n/2; for(int i=0;i<sz;i++)ans-=1ll*2*lnum[i]*rnum[i]*vec[i]; printf("%lld\n",ans); }
3238: [Ahoi2013]差异
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4447 Solved: 2057
[Submit][Status][Discuss]
Description
Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成