洛谷P1019(DFS+字符串处理)

题目描述

单词接龙是一个与咱们常常玩的成语接龙相相似的游戏,如今咱们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每一个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beastbeastbeast和astonishastonishastonish,若是接成一条龙则变为beastonishbeastonishbeastonish,另外相邻的两部分不能存在包含关系,例如atatat 和 atideatideatide 间不能相连。linux

输入输出格式

输入格式:ios

输入的第一行为一个单独的整数nnn (n≤20n \le 20n≤20)表示单词数,如下nnn 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你能够假定以此字母开头的“龙”必定存在.c++

输出格式:windows

只需输出以此字母开头的最长的“龙”的长度数组

分析

这是个深搜的题,大致的思路并不难,就是不断的搜,保证每一个单词出现次数不超过2次,而后每次更新res。但这个题目要仔细地看,比较考细节。这里要预处理,用一个二维数组存储单词i后接单词j的最小重叠部分的长度,这样就不用每次搜索的时候都计算一遍。要注意!!是最小重叠部分,而不是重叠部分,这应该是贪心思想,由于是求最大长度,因此固然应该取最小重叠部分。好比单词add,dda,将他们链接成addda就比adda要长。具体的预处理操做在check函数中,预处理的结果存储在re数组中。还有不能够包含:一旦包含了和不用岂不是同样。因此就只用判断上一个链接的单词和当前要链接的单词的最小重叠部分就能够了。ide

(另外,耽误我特别久时间的这个字符数组和string,还有char的输入问题,c++大佬绕行便可。我尚未来得及系统地学习c++,写代码很难受。。string类型用scanf输入的时候要先resize限制大小,这样我试了一下,就无法用length()获取到它的实际长度了,这里我还不知道怎么解决,留坑待填。我就先用cin来输入,cin能够直接输入,不会有任何问题。函数

还有一个就是关于char的输入的问题:学习

scanf读取int等内容时,读取完一个数据后后面的空格回车仅仅是表示这个数据输入完了,没有略过,它一直存在于输入缓冲区,而到了下一个数据,若也是int或字符串什么的,会自动把空格回车略过,直到找到应输入的内容,而后读取。而char不一样,它不忽略本该读取的数据前面的空白,就把那空格或回车读进去了,若是是有多个空格回车,剩下那些空白就依然还存在与缓冲区,给下一个char读取了。
有如下几种解决方案:idea

    使用fflush(stdin)。scanf读取char以前,用fflush(stdin)空输入缓冲区,使其为空,而后输入char就不会有空白符阻碍了。
    scanf读取char以前使用getchar()把’\n’和’ ‘吃了,getchar()不跳过空格回车。固然getchar()几回,即吃几个字符也是个问题,因此本方法仅适用于好比竞赛题什么的,输入格式都很标准,不会出现不定多少的空格回车(但我用了一次以后发现这个也很差用,有个问题时windows下换行符为\r或\n,linux是\n,而MacOS是\r,你不知道后台测评的是什么系统,因此通常gets,getchar都要慎用。不过如今想一想好像scanf和getchar功能同样欸,都不跳空白的。那么这么说来直接用scanf吸取空格应该也是能够的,好比scanf(“%c%c”,&ch,&ch)可是好像没见人用过。
    VJ上看到这个写法scanf(“\n%c”,ch),即在每一个输入char的scanf里的%c前面,都加上\n。对于在scanf里不单单写%c还写其余的东西,就像输出同样,我没细研究,就知道好比scanf(“233%c666”,ch)我必须先输入233再输入char再输入666,想一想好像也有道理,以此来忽略\n,可是我发现它也能够忽略空格。虽然不是很懂,空格和回车在编译器眼里同样吗?因而试了试spa

scanf(” %c”,ch),也同样的效果!因而这就达到了与输入int、double同样的格式,任意输入多少空格和回车都没事了。如今也是这么在用,虽然其实我不是很懂其原理。)

 

下面是AC代码:

#include<cstdio>
#include<cstdlib>
#include<string>
#include<algorithm>
#include<iostream>
using namespace std;

int n;                 //单词数
int res=0;             //当前的最长长度
string word[25];       //存放单词
int re[25][25];        //存储两个单词的最小重叠部分
int t[25];             //记录第i个单词的剩余使用次数

int check(int x,int y){ //计算第x个单词后接第y个单词的最小重叠部分
	int len1=word[x].length(),len2=word[y].length();
	int a=min(len1,len2)-1;
	int flag;
	for(int i=1;i<=a;i++){
		flag=1;
		for(int j=0;j<i;j++){
			if(word[x][len1-i+j]!=word[y][j]){
				flag=0;
				break;
			}
		}
		if(flag)       //若获得最小的重叠部分,直接返回
			return i;
	}
	return 0;		
}

void dfs(int sum,int pre){ //深搜
	res=max(res,sum);      //更新当前的最长长度
	for(int i=0;i<n;i++){
		int tmp=re[pre][i];//上一个单词后接第i个单词的最小重叠部分长度
		if(tmp&&t[i]){
			t[i]--;
			dfs(sum+word[i].length()-tmp,i);
			t[i]++;        //回溯
		}
	}
}

int main(){
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		cin>>word[i];
		t[i]=2;
	}
	for(int i=0;i<n;i++)  //预处理
		for(int j=0;j<n;j++)
			re[i][j]=check(i,j);
	char c;
	scanf(" %c",&c);      //在%c前加一个空格,避免scanf将空格吃掉
	for(int i=0;i<n;i++){ 
		if(word[i][0]==c){
			t[i]--;
			dfs(word[i].length(),i);
			t[i]++;      //回溯
		}
	}
	printf("%d\n",res);
	return 0;
}

体会:要先观察能不能预处理,避免重复地计算。注意审题,慎用gets,getchar。