【NOI2016】优秀的拆分 题解
题目大意:
求一个字符串中形如AABB的子串个数。
思路:
主要的解题思路参考了这篇题解【bzoj4650 [NOI2016]优秀的拆分】,讲得非常详细,还有画图说明,应该很好理解,这里也就不赘述了。但是我并不会SA这种后缀数组+st表的高级算法,在求LCS和LCP时直接用了二分+hash,因此时间复杂度是O(nlog2n)。不过听说还有一种用了Lyndon 分解进行预处理的二分+hash做法,时间复杂度也是O(nlogn),有兴趣也可以了解一下:题解 P1117 【[NOI2016]优秀的拆分】。当然不管哪种做法其实计数的思路都是一样的,可以直接去看第一篇题解的分析,不一样的只是求LCS和LCP的方法。
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<iostream> 4 using namespace std; 5 #define gethash(l,r) (Hash[r]-Hash[l-1]*mi[r-(l)+1]%mod+mod)%mod 6 const int S=999983,mod=100000009,M=30005; 7 long long h[M],t[M],Hash[M],mi[M]; 8 char s[M]; 9 int len; 10 11 bool check(int f,int a,int b,int l) 12 { 13 if (f) 14 if (a<l) return 0; 15 else return gethash(a-l+1,a)==gethash(b-l+1,b); 16 if (b+l-1>len) return 0; 17 return gethash(a,a+l-1)==gethash(b,b+l-1); 18 } 19 20 int found(int l,int r,int f,int a,int b) 21 { 22 int ans,mid; 23 for (;l<=r;) 24 { 25 mid=l+r>>1; 26 if (check(f,a,b,mid)) l=mid+1,ans=mid; 27 else r=mid-1; 28 } 29 return ans; 30 } 31 32 int main() 33 { 34 int n; 35 for (mi[0]=n=1;n<M;++n) mi[n]=mi[n-1]*S%mod; 36 for (scanf("%d",&n);n--;) 37 { 38 int i,j,lcs,lcp,l; 39 long long sum=0; 40 scanf("%s",s+1); 41 len=strlen(s+1); 42 for (i=1;i<=len;++i) Hash[i]=(Hash[i-1]*S+s[i]-96)%mod; 43 for (i=0;i<=len;++i) h[i]=t[i]=0; 44 for (i=1;i<=len>>1;++i) 45 for (j=i<<1;j<=len;j=j+i) 46 if (s[j]==s[j-i]) 47 { 48 lcs=found(1,i,1,j-i,j); 49 lcp=found(1,i,0,j-i,j); 50 l=lcs+lcp-i+1; 51 if (l>=1) 52 { 53 ++h[j-i-lcs+1],--h[j-i-lcs+l]; 54 ++t[j+lcp-l+1],--t[j+lcp]; 55 } 56 } 57 for (i=1;i<=len;++i) h[i]+=h[i-1],t[i]+=t[i-1]; 58 for (i=1;i<len;++i) sum+=t[i]*h[i+1]; 59 printf("%lld\n",sum); 60 } 61 return 0; 62 }
我一直在繁华的苍凉中徘徊着,用一颗OI的心寻找着生命和宇宙的美妙与玄奥。

浙公网安备 33010602011771号