题意: 给定一个字符串,求一个最短的串要求没有在该字符串的子串中出现过,如果有多个,输出字典序最小的那一个。
我们可以首先考虑第一问,考虑每一个点,如果这个点会通过加上某个字母转移到,肯定是这个字母在当前位置之前出现的第一个位置,(如果没有出现就是从空字符串中转移而来)。所以感觉这是一个图,我们可以先新建一个点表示最开始求的串为空的状态,再新建一个点表示终止状态,那么我们就是通过一些字符的添加,来从开始状态走到终止状态。我们就可以按上面的方法建图,在枚举的时候维护一个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