「CF1303C Perfect Keyboard」

前置芝士

  1. 图的遍历:通过DFS或者BFS遍历全图.
  2. 前向星:用来存边,但是在本题用也可以用一个二维数组解决.

具体做法

先从判断YES和NO开始,可以发现如果一个字母与三个及以上不同的字母相邻时肯定是不合法的,每个字母与左右的字母连一条边以后如果产生一个长度大于2的环也是不合法的.所以最终合法的图中没有环,没有一个点连出两条以上的边,自然可以发现这就是一堆链了,所以可以从出度为1或0的点开始遍历,如果可以遍历全图自然就没有环了.

代码

#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;i>=last;--i)
using namespace std;
int N,M,T,tot;
int out[114541];//出度
char s[114514];//字符串
bool visit[114514];//遍历时不可以遍历两次同一个点,所以需要记录一下
int answer[114514];//记录答案
//链式前向星
struct Edge
{
	int next,to;
}edge[114514];
#define FOR(now) for(int _i_=head[now];_i_;_i_=edge[_i_].next)
#define SON edge[_i_].to
int cnt=0;
int head[1111];
bool p[233][233];//判断两字母是否相邻
void AddEdge(int form,int to)//加边
{
	edge[++cnt].to=to;
	edge[cnt].next=head[form];
	head[form]=cnt;
}
void DFS(int now)//DFS遍历
{
	if(visit[now])//如果访问过就不再访问
	{
		return;
	}
	visit[now]=1;//修改为已经访问
	answer[++tot]=now;//记录答案
	FOR(now)
	{
		DFS(SON);
	}
}
void work()
{
//注意初始化
	REP(i,'a','z')
	{
		out[i]=0;
		head[i]=0;
	}
	REP(i,'a','z')
	REP(j,'a','z')
	{
		p[i][j]=0;
	}
	cnt=0;
	tot=0;
	cin>>s;
	N=strlen(s)-1;
	REP(i,1,N)
	{
		if(!p[s[i]][s[i-1]])//如果原来没有相邻
		{
			//连边
			AddEdge(s[i],s[i-1]);
			AddEdge(s[i-1],s[i]);
			//两字母出度都++
			out[s[i]]++;
			out[s[i-1]]++;
			if(max(out[s[i]],out[s[i-1]])>2)//如果出度大于2了就是NO
			{
				printf("NO
");
				return;
			}
			p[s[i]][s[i-1]]=p[s[i-1]][s[i]]=1;//改为已经相邻
		}
	}
	REP(i,'a','z')
	{
		visit[i]=0;
	}
	REP(i,'a','z')//遍历全图
	{
		if(!visit[i])
		{
			if(out[i]<=1)//从出度为0,1的位置开始
			DFS(i);
		}
	}
	REP(i,'a','z')//如果有没有遍历到的点就是NO
	{
		if(!visit[i])
		{
			printf("NO
");
			return;
		}
	}
	printf("YES
");
	REP(i,1,tot)printf("%c",answer[i]);//输出答案
	printf("
");
}
int main()
{
	scanf("%d",&T);
	REP(i,1,T)
	work();
	return 0;
}

原文地址:https://www.cnblogs.com/Sxy_Limit/p/12302941.html