zoukankan      html  css  js  c++  java
  • 【bzoj1396】 识别子串

    http://www.lydsy.com/JudgeOnline/problem.php?id=1396 (题目链接)

    题意

      问字符串S每一位的最短识别子串是多长(识别子串指包含这个字符且只出现在S中一次的子串)。

    Solution

      很简单,搞出后缀数组以后,对于每一个后缀i,都可以求出从i向后延伸的最短识别子串,也就是${max(height[rank[i]],height[rank[i]+1])+1}$,注意一种情况,就是i与排在它相邻位置的后缀的lcp就等于它自己的长度,这种情况i是没有向后延伸的识别子串的。

      所以正着求一遍后缀数组,反着求一遍后缀数组,然后线段树区间修改每个后缀的最短识别子串覆盖范围内的点就可以了。

    细节

      太久没写线段树,都忘记要开4倍空间了→_→

    代码

    // bzoj1396
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<map>
    #define LL long long
    #define inf 2147483640
    #define Pi acos(-1.0)
    #define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
    using namespace std;
    
    const int maxn=100010;
    struct Segtree {int l,r,s,tag;}tr[maxn<<2];
    int sa[maxn],height[maxn],rank[maxn];
    char s[maxn];
    
    namespace Suffix {
    	int wa[maxn],wb[maxn],ww[maxn];
    	bool cmp(int *r,int a,int b,int l) {
    		return r[a]==r[b] && r[a+l]==r[b+l];
    	}
    	void da(char *r,int *sa,int n,int m) {
    		int i,j,p,*x=wa,*y=wb;
    		for (i=0;i<=m;i++) ww[i]=0;
    		for (i=1;i<=n;i++) ww[x[i]=r[i]]++;
    		for (i=1;i<=m;i++) ww[i]+=ww[i-1];
    		for (i=n;i>=1;i--) sa[ww[x[i]]--]=i;
    		for (p=0,j=1;p<n;j*=2,m=p) {
    			for (p=0,i=n-j+1;i<=n;i++) y[++p]=i;
    			for (i=1;i<=n;i++) if (sa[i]>j) y[++p]=sa[i]-j;
    			for (i=0;i<=m;i++) ww[i]=0;
    			for (i=1;i<=n;i++) ww[x[y[i]]]++;
    			for (i=1;i<=m;i++) ww[i]+=ww[i-1];
    			for (i=n;i>=1;i--) sa[ww[x[y[i]]]--]=y[i];
    			for (swap(x,y),p=x[sa[1]]=1,i=2;i<=n;i++)
    				x[sa[i]]=cmp(y,sa[i-1],sa[i],j) ? p : ++p;
    		}
    	}
    	void calheight(char *r,int *sa,int n) {
    		for (int i=1;i<=n;i++) rank[sa[i]]=i;
    		for (int k=0,i=1;i<=n;i++) {
    			if (k) k--;
    			int j=sa[rank[i]-1];
    			while (r[i+k]==r[j+k]) k++;
    			height[rank[i]]=k;
    		}
    	}
    }
    
    namespace SegTree {
    	void build(int k,int s,int t) {
    		tr[k].l=s,tr[k].r=t;
    		if (s==t) {tr[k].s=inf;return;}
    		int mid=(s+t)>>1;
    		if (s<=mid) build(k<<1,s,mid);
    		if (mid<t) build(k<<1|1,mid+1,t);
    		tr[k].s=min(tr[k<<1].s,tr[k<<1|1].s);
    	}
    	void pushdown(int k) {
    		int x=tr[k].tag;tr[k].tag=0;
    		if (x<tr[k<<1].s) tr[k<<1].s=tr[k<<1].tag=x;
    		if (x<tr[k<<1|1].s) tr[k<<1|1].s=tr[k<<1|1].tag=x;
    	}
    	void update(int k,int s,int t,int val) {
    		int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    		if (l==s && r==t) {if (tr[k].s>val) tr[k].s=tr[k].tag=val;return;}
    		if (tr[k].tag) pushdown(k);
    		if (t<=mid) update(k<<1,s,t,val);
    		else if (s>mid) update(k<<1|1,s,t,val);
    		else update(k<<1,s,mid,val),update(k<<1|1,mid+1,t,val);
    		tr[k].s=max(tr[k<<1].s,tr[k<<1|1].s);
    	}
    	int query(int k,int p) {
    		int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1;
    		if (l==r && l==p) return tr[k].s;
    		if (p<=mid) return query(k<<1,p);
    		else return query(k<<1|1,p);
    	}
    }
    
    int main() {
    	using namespace Suffix;
    	using namespace SegTree;
    	scanf("%s",s+1);
    	int n=strlen(s+1);
    	da(s,sa,n,300);
    	calheight(s,sa,n);
    	build(1,1,n);
    	for (int i=1;i<=n;i++) {
    		int val=max(height[rank[i]],height[rank[i]+1]);
    		if (val==n-i+1) continue;
    		update(1,i,i+val,val+1);
    	}
    	for (int i=1;i<=n/2;i++) swap(s[i],s[n-i+1]);
    	da(s,sa,n,300);
    	calheight(s,sa,n);
    	for (int i=1;i<=n;i++) {
    		int val=max(height[rank[i]],height[rank[i]+1]);
    		if (val==n-i+1) continue;
    		update(1,n-(i+val)+1,n-i+1,val+1);
    	}
    	for (int i=1;i<=n;i++) printf("%d
    ",min(n,query(1,i)));
    	return 0;
    }
    
  • 相关阅读:
    小程序自定义日历组件
    uni-app 区分环境
    uniapp开发微信小程序获取用户手机号
    flex布局中子元素宽度失效的问题
    使用 VSCode 创建 SpringBoot RESTful 增删改查接口项目并发布
    [译] 如何使用 WebGL 技术进行风力地图可视化
    Cesium Primitive API 实践:绘制一个三角形
    ArcPy 输出路径神坑:不能有短横线
    【问题记录】本地没有更新远程仓库的jar包
    k8s 的pod按照时间排序
  • 原文地址:https://www.cnblogs.com/MashiroSky/p/6284267.html
Copyright © 2011-2022 走看看