【GDKOI2013】Country(回文自动机)(最小链覆盖)

给定一个长度为n的字符串s,现在要从s中选出尽量多的子串,满足选出的子串都是回 文的,并且不存在一个串是另外一个串的子串的情况。

n ≤ 300000


题解:

建出PAM求这个DAG上的最长反链。

这道题的网络流复杂度是可以被卡满的,所以我也不知道这个数据范围是怎么回事,可能出题人有什么高妙做法?

原来以为PAM图上面最小链覆盖等于最小路径覆盖,但是并不。

反例:cabaccddabad


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout; 
cs int N=7e5+5;

int S,T;
namespace NetWork{
	int n;
	struct edge{
		int to,cap,rev;
		edge(int _to,int _cap,int _rev):to(_to),cap(_cap),rev(_rev){}
	};
	std::vector<edge> G[N];
	typedef std::vector<edge>::iterator iter;
//	iter cur[N];
	
	inline void addedge(int u,int v,int val){
	//	cout<<u<<" "<<v<<"\n";
		G[u].push_back(edge(v,val,G[v].size()));
		G[v].push_back(edge(u,0,G[u].size()-1));
	}
	
	int lev[N],gap[N];
	
	int dfs(int u,cs int &flow){
		if(u==T)return flow;
		int ans=0;
	//	cerr<<"u : "<<u<<" "<<cur[u]-G[u].begin()<<"\n";
		for(iter e=G[u].end()-1;e!=G[u].begin()-1;--e)
		if(e->cap&&lev[e->to]+1==lev[u]){
			int delta=dfs(e->to,std::min(flow-ans,e->cap));
			e->cap-=delta;
			G[e->to][e->rev].cap+=delta;
			ans+=delta;
			if(ans==flow)return ans;
		}
	//	cout<<u<<" to : this place "<<lev[u]<<" "<<gap[lev[u]]<<"\n";
		if(0==--gap[lev[u]]){
			lev[0]=0x3f3f3f3f;
		//	cout<<"!!!!\n";
		//	system("pause");
		}
		++gap[++lev[u]];
	//	cur[u]=G[u].begin();
		return ans;
	}
	
	int ISAP(){
		int mxflow=0;
	//	BFS();
		memset(gap,0,sizeof gap);
		memset(lev,0,sizeof lev);
		gap[0]=n-2;
	//	for(int re i=1;i<=n;++i)cur[i]=G[i].begin();
		while(lev[0]!=0x3f3f3f3f){
			mxflow+=dfs(S,0x3f3f3f3f);
		}
		return mxflow;
	}
}

char s[N];
int n;

namespace PAM{
	int son[N][26],fa[N],len[N],now,last;
	
	inline void init(){len[1]=-1,fa[0]=1;now=1;}
	
	inline void push_back(char c,int i){c-='a';
		int p=last;
		while(s[i]!=s[i-len[p]-1])p=fa[p];
		if(!son[p][c]){
			len[++now]=len[p]+2;
			int k=fa[p];
			while(s[i]!=s[i-len[k]-1])k=fa[k];
			fa[now]=son[k][c],son[p][c]=now;
		}
		last=son[p][c];
	}
	
	inline void solve(){
		S=2*now+1,T=2*now+2;
		for(int re i=2;i<=now;++i){
			NetWork::addedge(S,i,1);
			NetWork::addedge(i+now,T,1);
			if(fa[i]>1){
				NetWork::addedge(fa[i],i+now,1);
			}
			for(int re c=0;c<26;++c)if(son[i][c]){
				NetWork::addedge(i,son[i][c]+now,1);
			}
		//	NetWork::addedge(i+now,i,0x3f3f3f3f);
		}
	//	cerr<<"???\n";
	}
}

signed main(){
//	freopen("country.in","r",stdin);freopen("country.out","w",stdout);
	PAM::init();
	scanf("%s",s+1);//double t=clock();
	n=strlen(s+1);
	for(int re i=1;i<=n;++i)PAM::push_back(s[i],i);
	PAM::solve();
//	cerr<<"???\n";
	NetWork::n=PAM::now*2+2;
//	cerr<<PAM::now<<"\n";
	cout<<PAM::now-1-NetWork::ISAP()<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值