arc 081 E Don't Be a Subsequence

        题意: 给定一个字符串,求一个最短的串要求没有在该字符串的子串中出现过,如果有多个,输出字典序最小的那一个。

         我们可以首先考虑第一问,考虑每一个点,如果这个点会通过加上某个字母转移到,肯定是这个字母在当前位置之前出现的第一个位置,(如果没有出现就是从空字符串中转移而来)。所以感觉这是一个图,我们可以先新建一个点表示最开始求的串为空的状态,再新建一个点表示终止状态,那么我们就是通过一些字符的添加,来从开始状态走到终止状态。我们就可以按上面的方法建图,在枚举的时候维护一个pre数组,表示 字符 i上一次出现的位置,连边的时候可以无脑一点,对于每个点直接与26个字母上次出现的位置来连边,反正我连了你走不走和我就没关系了-_-,最短路即为答案了。

         加上字典序的限制怎么做呢...其实就是一个大水题,细节有点多而已。

         往转移考虑,我们肯定是贪心的选择使得每一次的选的字符字典序最小,但是又要满足这个点在最短路上,还要满足这个点能够通过之前选取的字符转移到。

         会dijikstra和使用数组的同学这时候就会了,(可惜我都不会),通过从开头点和结尾点两遍最短路,判断dis1[a]+dis2[a]是否等于最短路值,接着按照与开头点的距离为顺序来转移,如果该点能从最优的方法来转移过来,而且转移它的点也能满足最优转移的时候把该点的vis标为1表示合法即可,还有就是正着跑和反着跑的时候要分开建图,防止后面的点转移到前面的点了。

          最zz的是我dijikstra先取的距离值的大的竟然过了样例,测大数据的时候发现正反最短路不相等zz的调了好久。

          好像是唯一几道自己独立做出来的arc的e题诶。

          看起来不难但是还是没在考试的时候调出来,我的做法细节太多了,ranwen想到了一种很优越的dp做法。

           下附AC代码。

Copy
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#include<vector>
#define maxn 200005
using namespace std;
struct nod
{
	int now,dis;
	nod(int x,int y)
	{
		now=x;
		dis=y;
	}
	nod(){}
};
bool operator<(nod a,nod b)
{
	return a.dis>b.dis;
}
int n,ss,t,tot[2];
int pre[maxn];
char s[maxn];
int head[2][maxn],nex[2][maxn*30],to[2][maxn*30],vis[maxn];
void add(int f,int x,int y)
{
	to[f][++tot[f]]=y; nex[f][tot[f]]=head[f][x]; head[f][x]=tot[f];
}
int pre2[maxn][30],id[maxn];
int dis1[maxn],dis2[maxn];
priority_queue<nod> q;
vector<nod>edge[maxn];
void spfa(int start,int f,int *dis)
{
	memset(dis,0x3f,sizeof(int)*maxn);
	memset(vis,0,sizeof(vis));
 
	dis[start]=0;
	q.push(nod(start,0));
	while(!q.empty())
	{
	//	cerr<<"+1"<<endl;
		int now=q.top().now; q.pop();
		if(vis[now]) continue;
		vis[now]=1;
	//	cout<<now<<" "<<dis[now]<<endl;
		for(int i=head[f][now];i;i=nex[f][i])
		{
			if(dis[to[f][i]]>dis[now]+1)
			{
				dis[to[f][i]]=dis[now]+1;
				q.push(nod(to[f][i],dis[to[f][i]]));
			}
		}
	}
}
int cmp(int i,int j)
{
	return dis1[i]<dis1[j];
}
int can[30];
int vis1[maxn];
int main()
{
	scanf("%s",s+1); n=strlen(s+1);
 
	int ss=n+1,t=n+2;
 
	for(int i=0;i<=25;i++)
		pre[i]=ss;
 
	for(int i=1;i<=n;i++)
	{
		id[i]=i;
		for(int j=0;j<=25;j++)
		if(!vis[pre[j]])
		{
			add(0,pre[j],i);
			add(1,i,pre[j]);
			vis[pre[j]]=1;
			edge[pre[j]].push_back(nod(i,j));
		}
 
		for(int j=0;j<=25;j++)
			vis[pre[j]]=0,pre2[i][j]=pre[j];
 
		pre[s[i]-'a']=i;
	}
 
	for(int i=0;i<=25;i++)
		if(!vis[pre[i]])
			add(0,pre[i],t),add(1,t,pre[i]),vis[pre[i]]=1,edge[pre[i]].push_back(nod(t,i));
	for(int i=0;i<=25;i++)
		vis[pre[i]]=0,pre2[t][i]=pre[i];
 
	spfa(ss,0,dis1);
	spfa(t,1,dis2);
 
	int res=dis1[t];
 
	int now=1;
 
 
	sort(id+1,id+1+n,cmp);
 
	int preans=0;
 
	vis1[ss]=1;
	for(int i=1;i<=n;i++)
	{
		if(now==res) 
			break;
 
		int temp=i;
 
		int p=id[i];
		while(dis1[p]==now)
		{
			for(int j=0;j<=25;j++)
			{
				if(dis1[p]+dis2[p]==res && dis1[p]==dis1[pre2[p][j]]+1 && vis1[pre2[p][j]])
					can[j]=1;
			}
			i++;
			p=id[i];
		}
	
 
		for(int j=0;j<=25;j++)
		if(can[j])
		{
			printf("%c",'a'+j);
			preans=j;
			break;
		}
 
		for(int j=temp;j<=i-1;j++)
		{
			p=id[j];
//			cerr<<dis1[p]<<" ";
			if(dis1[p]+dis2[p]==res && dis1[p]==dis1[pre2[p][preans]]+1 && vis1[pre2[p][preans]])
				vis1[p]=1;
		}
 
		memset(can,0,sizeof(can));
		i--;
 
		now++;
	}
 
	for(int i=0;i<=25;i++)
	{
		if(dis1[t]==dis1[pre2[t][i]]+1 && vis1[pre2[t][i]])
			return printf("%c\n",'a'+i),0;
	}
}

如果还在这里找彩蛋就太傻了 Especially For U      

By ZRX       



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值