[bzoj4556] [Tjoi2016&Heoi2016]字符串
Posted CHADLZX
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[bzoj4556] [Tjoi2016&Heoi2016]字符串相关的知识,希望对你有一定的参考价值。
翻转原串,建后缀自动机.
然后先考虑最朴素的思路,找到d所对应的节点,然后一直往上走,并更新答案.
发现由于有a,b的限制,更新答案需要取min,很不爽,不如二分答案.
然后就可以转化为判定性问题,用字符串定位技术找到当前的cd对应的字符串(其实就是倍增+len判定),
判定当前的节点是否有当前a,b范围的right集.
要知道每个节点的right集具体是什么用dfs序建立主席树即可.
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<ctime> #include<string> #include<iomanip> #include<algorithm> #include<map> using namespace std; #define LL long long #define FILE "dealing" #define up(i,j,n) for(int i=j;i<=n;++i) #define db double #define ull unsigned long long #define eps 1e-10 #define pii pair<int,int> int read(){ int x=0,f=1,ch=getchar(); while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();} while(ch>=‘0‘&&ch<=‘9‘){x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();} return f*x; } const int maxn=200200,mod=(int)(1e9+7+0.1),limit=(int)(1e6+1); int n,m; char s[maxn>>1]; struct node{ int y,next; }e[maxn<<1]; int len,linkk[maxn],pre[maxn],dfs_clock=0,low[maxn],r[maxn],dep[maxn],fa[maxn][26],id[maxn],zuo[maxn],val[maxn]; void insert(int x,int y){ e[++len].y=y; e[len].next=linkk[x]; linkk[x]=len; } namespace Sam{ int np,p,q,nq,pre[maxn],c[maxn][26],len[maxn],cnt,now,count[maxn],sa[maxn]; int extend(int x){ p=now;now=np=++cnt;len[np]=len[p]+1; while(p&&!c[p][x])c[p][x]=np,p=pre[p]; if(!p)pre[np]=1; else { q=c[p][x]; if(len[q]==len[p]+1)pre[np]=q; else { len[nq=++cnt]=len[p]+1; memcpy(c[nq],c[q],sizeof(c[q])); pre[nq]=pre[q]; pre[q]=pre[np]=nq; while(p&&c[p][x]==q)c[p][x]=nq,p=pre[p]; } } return np; } void build(){ now=1,cnt=1; up(i,1,n)val[extend(s[i]-‘a‘)]=i; up(i,1,cnt)zuo[val[i]]=i; } void prepare(){ up(i,1,cnt)count[len[i]]++; up(i,1,cnt)count[i]+=count[i-1]; for(int i=cnt;i>=1;i--)sa[count[len[i]]--]=i; for(int i=cnt;i>=2;i--)insert(pre[sa[i]],sa[i]),insert(sa[i],pre[sa[i]]); } }; namespace chair_man_tree{ const int maxn=3000000; int c[maxn][2],sum[maxn],root[maxn],n,cnt=0,L,R; void updata(int x){sum[x]=sum[c[x][0]]+sum[c[x][1]];} void insert(int x,int& o,int key,int l,int r){ if(key==0){o=x;return;} o=++cnt; if(l==r){ sum[o]=sum[x]+1; return; } int mid=(l+r)>>1; if(key>mid){ c[o][0]=c[x][0]; insert(c[x][1],c[o][1],key,mid+1,r); } else { c[o][1]=c[x][1]; insert(c[x][0],c[o][0],key,l,mid); } updata(o); } void build(){ up(i,1,n)insert(root[i-1],root[i],r[i],1,n); } int query(int x,int o,int l,int r){ //printf("%d %d %d\n",l,r,sum[o]-sum[x]); if(l>R||r<L)return 0; if(l>=L&&r<=R)return sum[o]-sum[x]; if(!(sum[o]-sum[x]))return 0; int mid=(l+r)>>1; return query(c[x][0],c[o][0],l,mid)+query(c[x][1],c[o][1],mid+1,r); } int Query(int l,int r,int Le,int Ri){ L=Le,R=Ri; //cout<<L<<" "<<R<<endl; return query(root[l],root[r],1,n); } }; void dfs(int x){ pre[x]=++dfs_clock; //printf("%d %d %d %d\n",x,pre[x],val[x],fa[x][0]); for(int i=linkk[x];i;i=e[i].next){ if(e[i].y==fa[x][0])continue; dep[e[i].y]=dep[x]+1; fa[e[i].y][0]=x; dfs(e[i].y); } low[x]=dfs_clock; } bool check(int a,int b,int c,int d){//返回c,d位置是否有right集有a,b的元素 int y=zuo[d]; for(int i=25;i>=0;i--) if(Sam::len[fa[y][i]]>=c)y=fa[y][i];//定位c,d字符串 int sum=chair_man_tree::Query(pre[y]-1,low[y],a,b); if(sum)return 1; else return 0; } int main(){ freopen(FILE".in","r",stdin); freopen(FILE".out","w",stdout); n=read(),m=read(); scanf("%s",s+1); reverse(s+1,s+n+1); Sam::build(); Sam::prepare(); dfs(1); up(i,1,Sam::cnt)id[pre[i]]=i;//主席树对应树 up(i,1,Sam::cnt)r[i]=val[id[i]]; chair_man_tree::n=Sam::cnt; chair_man_tree::build(); for(int j=1;j<=25;j++) for(int i=1;i<=Sam::cnt;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; while(m--){ int a=n-read()+1,b=n-read()+1,c=n-read()+1,d=n-read()+1; swap(a,b);swap(c,d); int left=1,right=min(d-c+1,b-a+1),ans=0; while(left<=right){ int mid=(left+right)>>1; if(check(a+mid-1,b,mid,d))left=mid+1,ans=mid; else right=mid-1; } printf("%d\n",ans); } return 0; }
以上是关于[bzoj4556] [Tjoi2016&Heoi2016]字符串的主要内容,如果未能解决你的问题,请参考以下文章
Bzoj4556: [Tjoi2016&Heoi2016]字符串 后缀数组
[BZOJ4556][Tjoi2016&Heoi2016]字符串 后缀数组+主席树
[BZOJ4556][TJOI2016&&HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)
bzoj 4556: [Tjoi2016&Heoi2016]字符串