NOI2016 优秀的拆分(图解)
2017-12-27 18:53
323 查看
如果一个字符串可以被拆分为 AABB 的形式,其中 A和 B是任意非空字符串,则我们称该字符串的这种拆分是优秀的。
例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
现在给出一个长度为 n的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。
3.字符串本身也是它的一个子串。
95分 O(n^2)暴力哈希处理f[i]以i为起始的AA串数量。
统计答案时O(n^2)暴力枚举AA串,与后面的f【i】匹配计入答案。
这个真的想不出来啊。。我们95分的做法慢在处理AA串的速度上。如果我们能够快速得到以i点为结尾的AA串个数和以i+1点为起始的AA点个数,那就能直接计算答案。
枚举A串的长度,每段长度设置一个计算点,我们发现一个AA串的长度为2*L,所有的长度为2*L的AA串上一定会有2个标记。我们考虑两个标记分别在2个A串上,如果这两个点在一个AA串上,两个点为对应点时分别计算这两个点的LCP和LCS。得到的这两个点的两端区间是相同的。
我们发现这种情况时恰好有一个AA串,以前面那个点(记作pre)-LCS+1为起点的串。
即:LCS+LCP>len时 才会有这样的AA串。
我们发现这两个区间有交时,两个对应点相距距离len。所以pre-LCS+1与当前点now-LCS+1是对应点。
我们完全可以找到这样两个长度为len相同的区间。
如图两端区间。然后这段区间是可以进行平移的。所以开始区间为【pre-LCS+1,pre+LCP-len】。
结束区间自己思考一下吧qwqwq。
然后进行差分最后前缀和就可以快速求出所需了。
例如,对于字符串 aabaabaa,如果令 A=aab,B=a,我们就找到了这个字符串拆分成 AABB的一种方式。
一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。比如我们令 A=a,B=baa,也可以用 AABB表示出上述字符串;但是,字符串 abaabaa 就没有优秀的拆分。
现在给出一个长度为 n的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。这里的子串是指字符串中连续的一段。
以下事项需要注意:
1.出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被记入答案。
2.在一个拆分中,允许出现 A=B。例如 cccc 存在拆分 A=B=c。
3.字符串本身也是它的一个子串。
95分 O(n^2)暴力哈希处理f[i]以i为起始的AA串数量。
统计答案时O(n^2)暴力枚举AA串,与后面的f【i】匹配计入答案。
这个真的想不出来啊。。我们95分的做法慢在处理AA串的速度上。如果我们能够快速得到以i点为结尾的AA串个数和以i+1点为起始的AA点个数,那就能直接计算答案。
枚举A串的长度,每段长度设置一个计算点,我们发现一个AA串的长度为2*L,所有的长度为2*L的AA串上一定会有2个标记。我们考虑两个标记分别在2个A串上,如果这两个点在一个AA串上,两个点为对应点时分别计算这两个点的LCP和LCS。得到的这两个点的两端区间是相同的。
我们发现这种情况时恰好有一个AA串,以前面那个点(记作pre)-LCS+1为起点的串。
即:LCS+LCP>len时 才会有这样的AA串。
我们发现这两个区间有交时,两个对应点相距距离len。所以pre-LCS+1与当前点now-LCS+1是对应点。
我们完全可以找到这样两个长度为len相同的区间。
如图两端区间。然后这段区间是可以进行平移的。所以开始区间为【pre-LCS+1,pre+LCP-len】。
结束区间自己思考一下吧qwqwq。
然后进行差分最后前缀和就可以快速求出所需了。
#include<bits/stdc++.h> using namespace std; #define ll long long const int MAXN=5e4+5; const int MOD=19260817; const int base=31; int hash[MAXN]; ll mi[MAXN],u[MAXN],v[MAXN]; char s[MAXN]; ll gethash(int l,int r){ ll ans=0; ans=hash[r]-hash[l-1]*mi[r-l+1]; ans%=MOD;ans+=MOD;ans%=MOD; return ans; } void work(){ int zuo,you; memset(hash,0,sizeof(hash)); memset(u,0,sizeof(u)); memset(v,0,sizeof(v)); scanf("%s",s+1); int len=strlen(s+1); for(int i=1;i<=len;i++)hash[i]=(hash[i-1]*base+s[i])%MOD; for(int L=1;L*2<=len;L++){ for(int i=L+L;i<=len;i+=L){ int pre=i-L; if(s[i]!=s[pre])continue; int l=1,r=L,tmps=0; while(l<=r){//LCS int mid=(l+r)>>1; if(gethash(pre-mid+1,pre)==gethash(i-mid+1,i))l=mid+1,tmps=mid; else r=mid-1; } l=1,r=L;int tmpp=0; while(l<=r){ int mid=(l+r)>>1; if(gethash(pre,pre+mid-1)==gethash(i,i+mid-1)) l=mid+1,tmpp=mid; else r=mid-1; } if(tmps+tmpp>L){ u[pre-tmps+1]++;u[pre+tmpp-L+1]--; v[i-tmps+L]++;v[i+tmpp]--; } } } ll ans=0; for(int i=1;i<=len;i++){ u[i]+=u[i-1],v[i]+=v[i-1]; } for(int i=1;i<len;i++) ans+=v[i]*u[i+1]; printf("%lld\n",ans); } int main(){ int t; mi[0]=1;for(int i=1;i<=30002;i++)mi[i]=(mi[i-1]*base)%MOD; scanf("%d",&t); while(t--)work(); return 0; }
相关文章推荐
- BZOJ4650 : [NOI2016]优秀的拆分
- BZOJ4650:[NOI2016]优秀的拆分——题解
- [BZOJ4650][NOI2016]优秀的拆分(SAM构建SA)
- BZOJ 4650: [Noi2016]优秀的拆分 哈希+分块
- [BZOJ4650][NOI2016]优秀的拆分-后缀数组
- [BZOJ4650][NOI2016]优秀的拆分 各数据点解法
- 【BZOJ4650】【NOI2016】优秀的拆分(后缀数组)
- luogu1117 [NOI2016]优秀的拆分
- NOI2016 优秀的拆分 [后缀数组]
- bzoj4650: [Noi2016]优秀的拆分 hash
- 【NOI2016】优秀的拆分
- 4650: [Noi2016]优秀的拆分
- BZOJ 4650 [Noi2016]优秀的拆分 ——后缀数组
- [UOJ P219][NOI2016]优秀的拆分[95]
- BZOJ4650 : [Noi2016]优秀的拆分
- UOJ #219 [NOI2016 D1T1] 优秀的拆分 [95分]
- 【NOI2016】优秀的拆分
- [BZOJ]4650 优秀的拆分(Noi2016)
- LOJ#2083. 「NOI2016」优秀的拆分
- [BZOJ]4650 优秀的拆分(Noi2016)(哈希+二分)