zoukankan      html  css  js  c++  java
  • manacher(无讲解)

    BZOJ3325: [Scoi2013]密码

    https://lydsy.com/JudgeOnline/problem.php?id=3325

    分析:

    • 根据前i个字符和一些不等和相等条件就可以确定每一位。
    • 用manacher优化暴力的过程,发现就是manacher逆过来做。
    • 相等的赋值,不等的打标记。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    using namespace std;
    #define N 200050
    #define db(x) cerr<<#x<<" = "<<x<<endl
    char ans[N];
    int n,a[N],b[N],vis[30][N];
    int rp[N];
    int main() {
    	scanf("%d",&n);
    	int i,j;
    	for(i=1;i<=n;i++) scanf("%d",&a[i]);
    	for(i=1;i<n;i++) scanf("%d",&b[i]);
    	int ln=2*n+1;
    	for(i=1;i<=n;i++) {
    		rp[2*i-1]=b[i-1]+1;
    		rp[2*i]=a[i]+1;
    	}
    	rp[2*n+1]=1;
    	
    	int lst=1,mx=1;
    	for(i=1;i<=ln;i++) {
    		if(i&1) ans[i]='z'+1;
    		else if(!ans[i]) {
    			for(j=0;j<26;j++) if(!vis[j][i]) {
    				ans[i]=j+'a'; break;
    			}
    		}
    		int t=max(1,min(mx-i,rp[lst*2-i]));
    		for(;t<=rp[i];t++) {
    			if(i+t-1<=ln&&i-t+1>=1) ans[i+t-1]=ans[i-t+1];
    		}
    		if(i-t+1>=1&&i+t-1<=ln) vis[ans[i-t+1]-'a'][i+t-1]=1;
    		if(i+rp[i]-1>mx) {
    			mx=i+rp[i]-1;
    			lst=i;
    		}
    	}
    	for(i=2;i<=ln;i+=2) printf("%c",ans[i]);
    }
    

    BZOJ4166: 月宫的符卡序列

    https://lydsy.com/JudgeOnline/problem.php?id=4166

    分析:

    • 用manacher+hash暴力建立回文树并在上面DP即可。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <iostream>
    #include <tr1/unordered_map>
    #include <map>
    using namespace std;
    using namespace std::tr1;
    #define db(x) cerr<<#x<<" = "<<x<<endl
    #define N 1000050
    #define M 2000050
    #define base 131
    typedef unsigned long long ull;
    char w[N];
    int n,ln,head[N],to[N],nxt[N],cnt,fa[N],tot,s[M],rp[M];
    int val[N],ans;
    ull mi[N],h[N];
    unordered_map<ull,int>mp;
    inline void add(int u,int v) {
    	to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt;
    }
    ull get_h(int l,int r) {
    	return h[r]-h[l-1]*mi[r-l+1];
    }
    int work(int l,int r) {
    	ull now=get_h(l,r);
    	int p;
    	if(mp.count(now)) p=mp[now];
    	else p=mp[now]=++tot;
    
    
    	if(r-l+1<=1||fa[p]) return p;
    	
    	fa[p]=work(l+1,r-1);
    	add(fa[p],p);
    	return p;
    }
    void dfs(int x) {
    	int i;
    	for(i=head[x];i;i=nxt[i]) {
    		dfs(to[i]);
    		val[x]^=val[to[i]];
    	}
    	if(x!=1&&val[x]>ans) ans=val[x];
    }
    bool check(int l,int r) {
    	int i,j;
    	for(i=l,j=r;i<=j;i++,j--) if(w[i]!=w[j]) return 0;
    	return 1;
    }
    void solve() {
    	ans=0;
    	mp.clear();
    	memset(rp,0,sizeof(rp));
    	memset(val,0,sizeof(val));
    	memset(head,0,sizeof(head)); cnt=0;
    	memset(fa,0,sizeof(fa));
    
    	scanf("%s",w+1);
    	n=strlen(w+1);
    	int i;
    	for(mi[0]=i=1;i<=n;i++) {
    		mi[i]=mi[i-1]*base;
    		h[i]=h[i-1]*base+w[i];
    	}
    	mp[0]=1;
    	for(i=2;i<=27;i++) {
    		fa[i]=1; add(1,i); mp[i-2+'a']=i;
    	}
    	tot=27;
    
    	int mx=1,lst=1;
    	for(i=1;i<=n;i++) {
    		s[(i<<1)-1]='{';
    		s[(i<<1)]=w[i];
    	}
    	s[n*2+1]='{';
    	ln=n*2+1;
    
    	lst=mx=1;
    	for(i=1;i<=ln;i++) {
    		if(i<mx) rp[i]=min(mx-i+1,rp[lst*2-i]);
    		else rp[i]=1;
    		for(;i-rp[i]>=1&&i+rp[i]<=ln&&s[i-rp[i]]==s[i+rp[i]];rp[i]++);
    		int t=(rp[i])>>1;
    		if(i&1) {
    			if(i!=1&&t) {
    				int mid=i>>1;
    				val[work(mid-t+1,mid+t)]^=(mid-1);
    			}
    		}else {
    			if(t) {
    				int mid=i>>1;
    				val[work(mid-t+1,mid+t-1)]^=(mid-1);
    			}
    		}
    		if(i+rp[i]-1>mx) {
    			mx=i+rp[i]-1; lst=i;
    		}
    	}
    	dfs(1);
    	printf("%d
    ",ans);
    }
    int main() {
    	int T;
    	scanf("%d",&T);
    	while(T--) solve();
    }
    

    2342: [Shoi2011]双倍回文

    https://lydsy.com/JudgeOnline/problem.php?id=2342

    分析:

    • 首先我们选择的一定是一个回文串。
    • 这个限制去掉之后,只需令它的一半也是回文串。
    • 假设对称轴左边位置为(x),且(y)能成为其一半子串的右端点的条件是(y-rp_yle x)(yle x+rp_x/2)
    • (y-rp_y)排序然后用(set)维护前驱后继即可。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <set>
    using namespace std;
    #define N 1000050
    char w[N],s[N];
    int n,rp[N],f[N],ans,t[N];
    set<int>S;
    inline bool cmp(const int &x,const int &y) {return x-f[x] < y-f[y];}
    int main() {
    	scanf("%d%s",&n,w+1);
    	int i,ln=n*2+1;
    	for(i=1;i<=n;i++) s[2*i-1]='{', s[2*i]=w[i];
    	s[2*n+1]='{';
    	int mx=1,lst=1;
    	for(i=1;i<=ln;i++) {
    		rp[i]=min(mx-i,rp[lst*2-i]);
    		if(!rp[i]) rp[i]=1;
    		for(;i-rp[i]>=1&&i+rp[i]<=ln&&s[i-rp[i]]==s[i+rp[i]];rp[i]++) ;
    		if(i+rp[i]-1>mx) {
    			mx=i+rp[i]-1, lst=i;
    		}
    	}
    	for(i=1;i<n;i++) {
    		f[i]=rp[i*2+1]>>1;
    	}
    	int j=1;
    	for(i=1;i<n;i++) t[i]=i;
    	sort(t+1,t+n,cmp);
    	for(i=1;i<n;i++) {
    		int x=i;
    		for(;j<n&&t[j]-f[t[j]]<=x;j++) S.insert(t[j]);
    		set<int>::iterator it=S.upper_bound((f[x]>>1)+x);
    		if(it!=S.begin()) {
    			--it;
    			ans=max(ans,((*it)-x)*4);
    		}
    	}
    	printf("%d
    ",ans);
    }
    

    BZOJ5036: [Jsoi2014]回文串

    https://lydsy.com/JudgeOnline/problem.php?id=5036

    分析:

    • 先用其他字符将每个字符隔开方便统计。
    • 考虑([l,r])这段区间以(x)为中心的回文串个数。
    • 显然这玩意等于(min(x-l+1,rp[x],r-x+1))
    • 拆成两部分,每一部分用树状数组再拆开统计。

    代码:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cstdlib>
    using namespace std;
    typedef long long ll;
    #define N 200050
    #define M 300050
    char w[N],s[N];
    int n,rp[N],pi[N],Q,ln;
    ll ans[M];
    struct A {
    	int id,l,r,mid;
    }q[M];
    struct B {
    	ll c[N];
    	void fix(int x,int v) {for(;x<=ln;x+=x&(-x)) c[x]+=v;}
    	ll inq(int x) {ll re=0;for(;x;x-=x&(-x)) re+=c[x]; return re;}
    }t1,t2,t3;
    bool cmp1(const A &x,const A &y) {return x.l<y.l;}
    bool cmp2(const A &x,const A &y) {return x.r>y.r;}
    bool cmp3(const int &x,const int &y) {return x-rp[x]<y-rp[y];}
    bool cmp4(const int &x,const int &y) {return x+rp[x]>y+rp[y];}
    int main() {
    	scanf("%s%d",w+1,&Q);
    	n=strlen(w+1);
    	int i; ln=2*n+1;
    	for(i=1;i<=n;i++) s[2*i-1]='{',s[2*i]=w[i];
    	s[2*n+1]='{';
    	int mx=1,lst=1;
    	for(i=1;i<=ln;i++) {
    		rp[i]=min(mx-i,rp[2*lst-i]);
    		if(!rp[i]) rp[i]=1;
    		for(;i-rp[i]>=1&&i+rp[i]<=ln&&s[i-rp[i]]==s[i+rp[i]];rp[i]++);
    		if(i+rp[i]-1>mx) {
    			mx=i+rp[i]-1; lst=i;
    		}
    	}
    	// for(i=1;i<=ln;i++) printf("%d ",rp[i]); puts("");
    	for(i=1;i<=Q;i++) {
    		scanf("%d%d",&q[i].l,&q[i].r);
    		q[i].l=2*q[i].l-1, q[i].r=2*q[i].r+1;
    		q[i].id=i; q[i].mid=(q[i].l+q[i].r)>>1;
    	}
    	sort(q+1,q+Q+1,cmp1);
    	for(i=1;i<=ln;i++) {
    		t1.fix(i,rp[i]);
    		pi[i]=i;
    	}
    	sort(pi+1,pi+ln+1,cmp3);
    	int j=1;
    	for(i=1;i<=Q;i++) {
    		for(;j<=ln&&pi[j]-rp[pi[j]]+1<q[i].l;j++) {
    			t1.fix(pi[j],-rp[pi[j]]);
    			t2.fix(pi[j],1);
    			t3.fix(pi[j],pi[j]);
    		}
    		ans[q[i].id]+=t1.inq(q[i].mid)-t1.inq(q[i].l-1)
    		 			- (t2.inq(q[i].mid)-t2.inq(q[i].l-1))*(q[i].l-1)
    					+ (t3.inq(q[i].mid)-t3.inq(q[i].l-1));
    	}
    	memset(t1.c,0,sizeof(t1.c));
    	memset(t2.c,0,sizeof(t2.c));
    	memset(t3.c,0,sizeof(t3.c));
    	sort(q+1,q+Q+1,cmp2);
    	for(i=1;i<=ln;i++) t1.fix(i,rp[i]);
    	sort(pi+1,pi+ln+1,cmp4);
    	j=1;
    	for(i=1;i<=Q;i++) {
    		for(;j<=ln&&pi[j]+rp[pi[j]]-1>q[i].r;j++) {
    			t1.fix(pi[j],-rp[pi[j]]);
    			t2.fix(pi[j],1);
    			t3.fix(pi[j],pi[j]);
    		}
    		ans[q[i].id]+=t1.inq(q[i].r)-t1.inq(q[i].mid)
    					+ (t2.inq(q[i].r)-t2.inq(q[i].mid))*(q[i].r+1)
    					- (t3.inq(q[i].r)-t3.inq(q[i].mid));
    	}
    
    	for(i=1;i<=Q;i++) {
    		ans[q[i].id]=(ans[q[i].id]-((q[i].r-q[i].l)>>1)-1)>>1;
    	}
    	for(i=1;i<=Q;i++) printf("%lld
    ",ans[i]);
    }
    
  • 相关阅读:
    delphi for xx in xx do 语法的使用示例
    Delphi XE7的安卓程序如何调用JAVA的JAR,使用JAVA的类?
    ST Visual Programmer批量烧写教程
    关于FATFS文件系统挂载多个磁盘
    STM8不用手动复位进入自带Bootloader方法(串口下载)
    Linux下安装Eclipse
    微信平台二次开发实例讲解——三元篇
    架构设计深入学习01--概论与预架构阶段
    Linux tomcat安装详解
    程序员的自我修养——操作系统篇
  • 原文地址:https://www.cnblogs.com/suika/p/10016590.html
Copyright © 2011-2022 走看看