HDU4691用RMQ求所有字符串后缀的最长公共前缀
2017-07-26 21:09
351 查看
测试证明板子二虽然常数大。但是还是比板子一快的多。 板子1: 复杂度n*log*log #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define ll long long #define maxn 100010 using namespace std; char s[maxn]; int n,k,q; int rank[maxn],sa[maxn],tmp[maxn],lcp[maxn];//lcp:0-n-1 bool cmp(int x,int y){ if(rank[x]!=rank[y]) return rank[x]<rank[y]; int sx=x+k<=n ? rank[x+k]:-1; int sy=y+k<=n ? rank[y+k]:-1; return sx<sy; } void build_sa(){ n=strlen(s); for(int i=0;i<=n;i++){ sa[i]=i; rank[i]=i<n ? s[i]:-1; } for(k=1;k<=n;k<<=1){ sort(sa,sa+n+1,cmp); tmp[sa[0]]=0; for(int i=1;i<=n;i++){ tmp[sa[i]]=tmp[sa[i-1]]+(cmp(sa[i-1],sa[i]) ? 1:0); } for(int i=0;i<=n;i++) rank[i]=tmp[i]; } } void build_lcp(){ n=strlen(s); //for(int i=0;i<=n;i++) rank[sa[i]]=i; int h=0; lcp[0]=0; for(int i=0;i<n;i++){ int j=sa[rank[i]-1]; if(h>0) h--; for(;j+h<n&&i+h<n;h++){ if(s[j+h]!=s[i+h]) break; } lcp[rank[i]-1]=h; } } int dp[20][maxn],mm[maxn]; void init_RMQ(int n){ mm[0]=-1; for(int i=1;i<=n;i++){//长度1-n mm[i]=(i&(i-1)) ? mm[i-1]:mm[i-1]+1; } for(int i=0;i<n;i++) dp[0][i]=lcp[i]; for(int i=1;i<=mm ;i++){ for(int j=0;j+(1<<i)-1<n;j++){ dp[i][j]=min(dp[i-1][j],dp[i-1][j+(1<<i>>1)]); } } } int RMQ(int x,int y){//[x,y-1] if(x==y) return n-x; x=rank[x],y=rank[y]; if(x>y) swap(x,y); y--; int l=mm[y-x+1]; return min(dp[l][x],dp[l][y-(1<<l)+1]); } void read(){ scanf("%d",&q); ll sum1=0,sum2=0; int pl=-1,pr=-1,l,r; for(int i=0;i<q;i++){ scanf("%d%d",&l,&r); sum1+=(r-l+1); if(pl==-1){ sum2+=r-l+1; }else{ int LCP=RMQ(pl,l); int ans=min(LCP,min(r-l,pr-pl)); sum2+=(r-l-ans); if(ans==0) sum2+=1; else sum2+=(int)log10(ans*1.0)+1; } pl=l,pr=r; } printf("%I64d %I64d\n",sum1,sum2+2*q); } int main(){ while(~scanf("%s",s)){ build_sa(); build_lcp(); init_RMQ(n); read(); } return 0; } 板子2: 复杂度:n*log #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #define ll long long #define maxn 100010 using namespace std; char s[maxn]; int c[maxn],wa[maxn],wb[maxn],r[maxn];//求SA数组须要的中间变量,不须要赋值 //待排序的字符串放在s数组中,从s[0]到s[n-1],长度为n,且最大值小于m, //除s[n-1]外的全部s[i]都大于0,r[n-1]=0 //函数结束以后结果放在sa数组中 int n,sa[maxn],lcp[maxn],rank[maxn]; bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } void build_sa(int n,int m){//数组长度,最大数字 for(int i=0;i<=n;i++) r[i]=i<n ? s[i]:0; n++; int i,j,p,*x=wa,*y=wb; //第一轮基数排序。假设s的最大值非常大,可改为高速排序 for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[i]=r[i]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(j=1;j<=n;j<<=1){ p=0; //直接利用sa数组排序第二keyword for(i=n-j;i<n;i++) y[p++]=i;//后面的j个数第二keyword为空的最小 for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; //这样数组y保存的就是依照第二keyword排序的结果 //基数排序第一keyword for(i=0;i<m;i++) c[i]=0; for(i=0;i<n;i++) c[x[y[i]]]++; for(i=1;i<m;i++) c[i]+=c[i-1]; for(i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i]; //依据sa和x数组计算新的x数组 swap(x,y); p=1,x[sa[0]]=0; for(i=1;i<n;i++) x[sa[i]]=cmp(y,sa[i-1],sa[i],j)? p-1:p++; if(p>=n) break; m=p; } } void build_lcp(int n){ int i,j,k=0; for(i=0;i<=n;i++) rank[sa[i]]=i; lcp[0]=0; for(i=0;i<n;i++){ j=sa[rank[i]-1]; if(k) k--; while(s[i+k]==s[j+k]) k++; lcp[rank[i]-1]=k; } } int dp[20][maxn],mm[maxn]; void init_RMQ(int n){ mm[0]=-1; for(int i=1;i<=n;i++){ mm[i]=(i&(i-1)) ? mm[i-1]:mm[i-1]+1; } for(int i=0;i<n;i++) dp[0][i]=lcp[i]; for(int i=1;i<=mm ;i++){ for(int j=0;j+(1<<i)-1<n;j++){ dp[i][j]=min(dp[i-1][j],dp[i-1][j+(1<<i>>1)]); } } } int RMQ(int x,int y){ if(x==y) return n-x; x=rank[x],y=rank[y]; if(x>y) swap(x,y); y--; int l=mm[y-x+1]; return min(dp[l][x],dp[l][y-(1<<l)+1]); } int q; void read(){ scanf("%d",&q); ll sum1=0,sum2=0; int pl=-1,pr=-1,l,r; for(int i=0;i<q;i++){ scanf("%d%d",&l,&r); sum1+=(r-l+1); if(pl==-1){ sum2+=r-l+1; }else{ int LCP=RMQ(pl,l); //cout<<i<<":"<<LCP<<endl; int ans=min(LCP,min(r-l,pr-pl)); //cout<<i<<":"<<ans<<endl; sum2+=(r-l-ans); if(ans==0) sum2+=1; else sum2+=(int)log10(ans*1.0)+1; } pl=l,pr=r; } printf("%I64d %I64d\n",sum1,sum2+2*q); } int main(){ while(~scanf("%s",s)){ n=strlen(s); build_sa(n,128); build_lcp(n); /*for(int i=0;i<n;i++){ cout<<i<<" "<<sa[i]<<" "<<lcp[i]<<endl; }*/ init_RMQ(n); read(); } return 0; }
相关文章推荐
- 扩展KMP--求字符串S的所有后缀和字符串T的最长公共前缀
- HDU 2594 Simpsons’ Hidden Talents 两字符串前缀与后缀的最长公共部分
- hdu 4691 最长公共前缀 后缀数组 +lcp+rmq
- hdu4691(后缀数组求最长公共前缀)
- URAL1297Palindrome(最长回文子串 、后缀数组最长公共前缀+RMQ)
- 字符串相关处理kmp,前缀数,后缀树,后缀数组,最长回文串,最长重复字串,最长非重复字串
- hdoj 2594 Simpsons’ Hidden Talents 【KMP】【求串的最长公共前缀后缀】
- POJ 2774 Long Long Message(后缀数组 两个字符串的最长公共字串 )
- 实用算法实现-第 8 篇 后缀树和后缀数组 [3 两个字符串的最长公共子串]
- leetcode-14. Longest Common Prefix(寻找字符串数组最长公共前缀)
- POJ 3261 后缀数组 可重叠的,但是要重叠K次的 最长公共前缀最长是多少
- 字符串相关处理kmp,前缀数,后缀树,后缀数组,最长回文串,最长重复字串,最长非重复字串
- poj 2774 最长公共子串--字符串hash或者后缀数组或者后缀自动机
- PAT 1077. Kuchiguse (20) 求公共最长后缀, 字符串的整行读取
- leetcode--最长公共前缀--简单的字符串操作
- 北大OJ百练——4073:最长公共字符串后缀(C语言)
- 实用算法实现-第 8 篇 后缀树和后缀数组 [3 两个字符串的最长公共子串]
- 14. Longest Common Prefix (计算一组字符串最长公共前缀)
- CSU1632Repeated Substrings(后缀数组/最长公共前缀)
- leetcode_14. Longest Common Prefix 求字符串数组中所有字符串的最长前缀