hdu 4622 Reincarnation(后缀树组求子串个数)
2015-11-07 12:13
441 查看
题意:求字符串任意区间子串个数。
思路:
首先对整个字符串求一次sa[]以及height[],之后对于任意区间[L, R],遍历一遍sa[],只要起点在[L, R]内的后缀就需要进行统计法,不过有一个地方要特别注意的就是全部的sa[]不一定就是区间内的sa[],这是因为区间内的后缀比较时有额外的长度限制。可以证明遍历的过程要遵循如下的规则:
后缀s1和后缀s2现在是两个待比较的后缀,s1在前,s2在后,其起点都在区间[L, R]内,并设两串在区间中的长度为 len1, len2, 其全局的最长公共前缀为 lcp。现考虑在遍历sa[]时,如何从全局sa[]得到正确的局部sa[]:
1: lcp < len1 && lcp < len2 时说明两个串在未结束时就比较出了大小,全局和局部的sa[]统一,因此可以放心令s2作为下一个字典序后缀;
2: lcp >= len1 && lcp >= len2 时说明在其中一个串结束时,两个串对应字符都是相等的,这时需要根据len1和len2关系来决定,如果len1>len2那么就不用更换了;
3: lcp >= len1 && lcp < len2 时说明在其中一个串结束时,两个串对应字符都是相等的,由于s2的长度比s1长,因此字典序肯定大,因此需要更换当前的后缀;
4: lcp < len1 && lcp >= len2 时说明在其中一个串结束时,两个串对应字符都是相等的,由于s1的长度比s2长,因此字典序肯定大,因此不需更换当前的后缀。
其中2和4条件可以合并,如果4成立,那么必定有 len1 > len2,因此可以简化这个判断这个过程:if (len1 > len2 && lcp >= len2) 则不更换 else 更换。
直接理解就是给结果带来误差的情况只会是某个被lcp完全包含的后缀被排在了后面,那么它的正确位置应该是在最前面,由于在后面匹配的其长度仍未整个局部后缀的长度,而在选择下一个字典序后缀时屏蔽掉这个后缀即可。
思路:
首先对整个字符串求一次sa[]以及height[],之后对于任意区间[L, R],遍历一遍sa[],只要起点在[L, R]内的后缀就需要进行统计法,不过有一个地方要特别注意的就是全部的sa[]不一定就是区间内的sa[],这是因为区间内的后缀比较时有额外的长度限制。可以证明遍历的过程要遵循如下的规则:
后缀s1和后缀s2现在是两个待比较的后缀,s1在前,s2在后,其起点都在区间[L, R]内,并设两串在区间中的长度为 len1, len2, 其全局的最长公共前缀为 lcp。现考虑在遍历sa[]时,如何从全局sa[]得到正确的局部sa[]:
1: lcp < len1 && lcp < len2 时说明两个串在未结束时就比较出了大小,全局和局部的sa[]统一,因此可以放心令s2作为下一个字典序后缀;
2: lcp >= len1 && lcp >= len2 时说明在其中一个串结束时,两个串对应字符都是相等的,这时需要根据len1和len2关系来决定,如果len1>len2那么就不用更换了;
3: lcp >= len1 && lcp < len2 时说明在其中一个串结束时,两个串对应字符都是相等的,由于s2的长度比s1长,因此字典序肯定大,因此需要更换当前的后缀;
4: lcp < len1 && lcp >= len2 时说明在其中一个串结束时,两个串对应字符都是相等的,由于s1的长度比s2长,因此字典序肯定大,因此不需更换当前的后缀。
其中2和4条件可以合并,如果4成立,那么必定有 len1 > len2,因此可以简化这个判断这个过程:if (len1 > len2 && lcp >= len2) 则不更换 else 更换。
直接理解就是给结果带来误差的情况只会是某个被lcp完全包含的后缀被排在了后面,那么它的正确位置应该是在最前面,由于在后面匹配的其长度仍未整个局部后缀的长度,而在选择下一个字典序后缀时屏蔽掉这个后缀即可。
#include<iostream> #include<stdio.h> #include<math.h> #include <string> #include<string.h> #include<map> #include<queue> #include<set> #include<utility> #include<vector> #include<algorithm> #include<stdlib.h> using namespace std; #define eps 1e-8 #define pii pair<int,int> #define inf 0x3f3f3f3f #define rd(x) scanf("%d",&x) #define rd2(x,y) scanf("%d%d",&x,&y) #define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z) #define mo(x) memset(x,0,sizeof(x)) #define ll long long int #define ma(x,y) (x)>(y)?(x):(y) #define mi(x,y) (x)<(y)?(x):(y) #define mod 20071027 #define maxn 20010 #define maxm 10000001 int t1[maxn],t2[maxn],c[maxn]; bool cmp(int *r,int a,int b,int l){ return r[a]==r[b]&&r[a+l]==r[b+l]; } void da(int str[],int sa[],int rankk[],int height[],int n,int m){ n++; int i,j,p,*x=t1,*y=t2; for(int i=0;i<m;i++) c[i]=0; for(int i=0;i<n;i++) c[x[i]=str[i]]++; for(int i=1;i<m;i++) c[i]+=c[i-1]; for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i; for(int j=1;j<=n;j<<=1){ p=0; for(i=n-j;i<n;i++) y[p++]=i; for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j; 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]; 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; } int k=0; n--; for(i=0;i<=n;i++) rankk[sa[i]]=i; for(i=0;i<n;i++){ if(k) k--; j=sa[rankk[i]-1]; while(str[i+k]==str[j+k]) k++; height[rankk[i]]=k; } } int Rank[maxn],height[maxn]; int str[maxn]; int r[maxn],sa[maxn]; int x,n,t,q; char s[2005]; int main() { rd(t); while(t--){ scanf("%s",s); n=strlen(s); for(int i=0;i<n;i++) str[i]=s[i]-'a'+1; str =0; da(str,sa,Rank,height,n,30); rd(q); int l,r; while(q--){ rd2(l,r); l--;r--; int last=-1; int res=0; int d=0; for(int i=1;i<=n;i++){ if(sa[i]>=l&&sa[i]<=r){ if(last==-1){ res+=r+1-sa[i]; last=i; d=n-sa[i]; } else{ d=min(d,height[i]); int l1=r+1-sa[last]; int l2=r+1-sa[i]; int k=min(d,min(l1,l2)); res= res+r+1-sa[i]-k; //d=r+1-sa[i]; if(l1>l2&&d>=l2); else { last=i; d=n-sa[i]; } // last=sa[i]; } } else d=min(d,height[i]); } printf("%d\n",res); } } return 0; }
相关文章推荐
- Duilib源码分析(四)绘制管理器—CPaintManagerUI—(前期准备三)
- unity发射弓箭轨迹的实现
- 【Android】Fragment真正意义上的onResume和onPause
- 蛮力法之冒泡排序(C实现)
- origin Add multiple fitted curves in a Histogram
- 警告: 函数声明不是一个原型 [-Wstrict-prototypes]
- (异常分析)关于target is null for setProperty的问题总结!
- Android内的生命周期整理
- Python 知识检漏
- Linux主机安全防护系列(四)selinux关闭
- Android数据库(八)之使用LitePal聚合函数
- Android Studio安装过程常见问题图解及Eclipse如何导入工程到Android Studio
- Linux_RHEL7_LDAP、Autofs服务
- win8系统的无线密码如何查看?win8查看wifi密码介绍
- Eclipse Android项目 为控件添加了Id,但是在Java代码中提示xxx cannot be resolved or is not a field
- nginx upstream模块
- Android数据库(七)之LitePal查询艺术
- Linux:重要网址(包括发行版安装包的下载地址)
- uva 1401Remember the Word(trie树+dp)
- 一个简单的3DTouch、Peek和Pop手势Demo,附github地址