【泉州一中国庆集训day6】String
来源:互联网 发布:方伯谦 知乎 编辑:程序博客网 时间:2024/06/02 23:39
题目链接:http://v.qzyz.com/contest/293/problem/3
题目大意:给定一个长度为N的字符串S,有Q组询问,对于每组询问l和r,求字符串S有多少字串T与G=S[l,r]相似。相似的定义:两个字符串的长度len相等,对于任意i<=len,j<=len,满足Ti=Tj且Gi=Gj,或者Ti≠Tj且Gi≠Gj。
数据范围:N,Q<=50 000,字符集为(a,b,c,d,e,f,g,h,i,j)
题解:对于判断字符串相等的问题通常用哈希来求解。在本题中,两个字符串并不要求相等,只需要字母一一对应即可。我们记一个数组a,表示S中与当前位置字符相同的上一个位置的距离,即找到最小的a[i]使得S[i-a[i]]=S[i]。那么两个字符串相似就说明a相等。但是我们注意到,在每个字符第一次出现的位置,a的值不需要相等,我们把这些位置称为特殊点,在两个相似的字符串中,特殊点的位置也应该一一对应。由于字符集大小只有10,所以这样的点最多只有10个。
我们可以给数组a的所有后缀排个序,在比较两个后缀的时候,先找出所有的特殊点,把这些点的位置上的a当成0,然后进行比对。
具体的比较方法是:找出两个串的所有特殊点并排序,然后从前往后比较,直到找到第一个 i 满足两个串的第i+1个特殊点位置不一样或者第i个和第i+1个特殊点之间的串的hash值不一样。然后从第 i 个特殊点的位置起,二分两个串的最长公共前缀长度z,比较两个串的第z+1个位置。如果有一个串的第z+1个位置是特殊点(前面说了当成0),那么该串排前面,否则直接比较两个串的第z+1个位置的a值。如果直到其中一个串结束时两串仍然相等,那么较短的串排前面。
排好顺序后,对于询问 l 和 r ,从以第 l 个位置开始的后缀所在的排名分别往前后二分出左右端点,使得它们的最长公共前缀大于等于r-l+1,即可统计出答案。
时间复杂度O(10nlog^2n(排序)+10nlogn(询问))
(ps:在本题中我是使用了107作为底数和自然溢出来哈希并成功卡过了所有数据,但是建议读者还是多选几个数作为底数或者取模来提高正确率,否则说不定什么时候就被卡了。话说明明上周就想写这篇题解了我是怎么拖到现在的难得我还记得hhhh)
代码如下:
#include <algorithm>#include <cstring>#include <cstdio>using namespace std;const int p=107;int a[50005],b[10],c[50005][10],d[50005],f[50005],g[50005],s[50005], h[50005],i,n,q,j,x,y;char ch;bool Same(int x,int y,int len){ return f[x+len]-f[x]*g[len]==f[y+len]-f[y]*g[len];}bool check(int x,int y,int len){ if (x+len>n || y+len>n) return 0; int a1[10],a2[10],i; for (i=0;i<10;++i) { if (s[x]!=i) a1[i]=c[x][i]-x;else a1[i]=len+1; if (s[y]!=i) a2[i]=c[y][i]-y;else a2[i]=len+1; } sort(a1,a1+10); sort(a2,a2+10); int z=0; for (i=0;i<10 && a1[i]==a2[i] && a1[i]<=len+1;++i) if (z!=a1[i] && !Same(x+z,y+z,a1[i]-1-z)) return 0; else if ((z=a1[i])>len) return 1; return 0;}int lcp(int x,int y){ int l=1,r=n-max(x,y),mid,z=0; for (;l<=r;) { mid=(l+r)>>1; if (Same(x,y,mid)) z=mid,l=mid+1; else r=mid-1; } return z;}bool cmp(int x,int y){ int a1[10],a2[10],i; for (i=0;i<10;++i) { if (s[x]!=i) a1[i]=c[x][i]-x;else a1[i]=n+1-x; if (s[y]!=i) a2[i]=c[y][i]-y;else a2[i]=n+1-y; } sort(a1,a1+10); sort(a2,a2+10); int z=0; for (i=0;i<10 && a1[i]==a2[i];++i) if (z!=a1[i] && !Same(x+z,y+z,a1[i]-1-z)) break;else z=a1[i]; if (x+z>n || y+z>n) return x+z>n; z+=lcp(x+z,y+z); if (x+z>=n || y+z>=n) return x+z>=n; if (z+1==a1[i] || z+1==a2[i]) return z+1==a1[i]; return a[x+z+1]<a[y+z+1];}int main(){ scanf("%d%d\n",&n,&q); for (i=1;i<=n;++i) { scanf("%c",&ch); s[i]=ch-'a'; } scanf("\n"); g[0]=1;f[0]=0; for (i=1;i<=n;++i) { a[i]=i-b[s[i]]; b[s[i]]=i; f[i]=f[i-1]*p+a[i]; g[i]=g[i-1]*p; } for (i=0;i<10;++i) c[n+1][i]=n+1; for (i=n;i>=0;--i) { for (j=0;j<10;++j) c[i][j]=c[i+1][j]; c[i][s[i]]=i; } for (i=1;i<=n;++i) d[i]=i; sort(d+1,d+n+1,cmp); for (i=1;i<=n;++i) h[d[i]]=i; for (i=1;i<=q;++i) { scanf("%d%d\n",&x,&y);y-=x;x=h[x]; int l=1,r=x-1,mid,ansl=x,ansr=x; for (;l<=r;) { mid=(l+r)>>1; if (check(d[x],d[mid],y)) ansl=mid,r=mid-1; else l=mid+1; } l=x+1;r=n; for (;l<=r;) { mid=(l+r)>>1; if (check(d[x],d[mid],y)) ansr=mid,l=mid+1; else r=mid-1; } printf("%d\n",ansr-ansl+1); } return 0;}
- 【泉州一中国庆集训day6】String
- 【泉州一中国庆集训day4】破解
- 【泉州一中国庆集训day4】书稿
- 【泉州一中国庆集训day5】label
- 国庆集训day6 T2 欧拉函数求互质数个数
- 湖南集训Day6
- ACM集训day6
- 寒假集训 day6
- 省队集训DAY6
- 沈阳集训day6
- 北京集训DAY6
- qbxt国庆水题记day6
- 国庆音标集训
- 国庆集训总结
- 5月集训Day6考试
- 省队集训Round3 DAY6
- 国庆清北刷题冲刺班 Day6 上午
- 国庆清北刷题冲刺班 Day6 下午
- CodeForces 550A Two Substrings 简单题
- 卸载软件包
- 综合项目之闪讯破解(一)之 闪讯拨号用户名核心算法
- MySql实例
- Android之响应式编程RxJava/RxAndroid
- 【泉州一中国庆集训day6】String
- usaco 1.3 Ski Course Design
- 手动添加导入表修改EXE功能
- boost 内存管理
- 联想天逸F41a重装win7 x64
- c#中的不同文件中类的调用
- 模拟计算器
- android布局属性小结
- Problem 42 Coded triangle numbers(ifstream +getline)