单词接龙

单词接龙

题目

问题描述
  单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast和astonish,如果接成一条龙则变为beastonish,另外相邻的两部分不能存在包含关系,例如at 和 atide 间不能相连。
输入格式
  输入的第一行为一个单独的整数n (n<=20)表示单词数,以下n 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在.
输出格式
  只需输出以此字母开头的最长的“龙”的长度
样例输入
  5
  at
  touch
  cheat
  choose
  tact
  a
样例输入
  23
样例说明
  连成的“龙”为atoucheatactactouchoose

分析

以样例分析,给定第一个字符是a,所以我们就要遍历单词列表[at,touch,cheat,choose,tact]找出以字符a开头的,找出at,然后又以字符t找到touch,和tact,接下来就会像多叉树一样开始扩展,直到每个单词在龙中出现两次或者最后出现的单词没有子节点(像样例中给出的choose一样,单词表中没有下一个单词能与它相接了),然后找到这个多叉树中,从根节点到每个叶子节点的最长节点字符数,就是本题的解。根据树的特性可知,此题用深度优先搜索来做。
用三个单词来演试一下长度计算,单词列表为[touch,choose,tact],假定给定开头字母为t,如下图所示
在这里插入图片描述
具体细节请看代码注释。

代码

/**
	 * 
	 * @param words 单词数组
	 * @param firstWord 现在为首的单个字符
	 * @return	最长龙的数量
	 */
	public static int getResult(String words[],String firstWord) {
		//	定义数组存储每个单词的访问量
		int isVisited[] = new int[words.length];
		int max=0;
		int tmp;
		//	循环查找以firstWord开头的单词
		for(int i=0;i<words.length;i++) {
			
			if(firstWord.equals(words[i].substring(0, 1))) {
				//	找到后给此单词的访问量加1
				isVisited[i]++;
				//	以此单词开始进行深度搜索,搜得的长度赋给tmp
			    tmp=dfs(words, isVisited, words[i]);	    
			    //	将该单词的访问量还原,对下一个匹配单词进行深度搜索。
			    isVisited[i]--;
			    //	max中存最大长度
			    max = tmp>max?tmp:max;
				
			}
		}
		return max;
	}
	/**
	 * 
	 * @param words 单词数组
	 * @param isVisited	访问量数组
	 * @param preString	单词入口
	 * @return 龙的最长长度
	 * 	at
  		touch
		
  		cheat
  		choose
  		tact   以这几个单词为例解释
	 */
	public static int dfs(String words[],int isVisited[], String preString) {
		//	是否还有后面单词的标志位,如果遍历完单词数组还是人没有找到后继单词则此状态为false,直接输出次单词的长度。比如最后一个接到choose,
		//	单词列表中中没有以e、se、ose、oose、hoose为首的单词,所以直接返回choose的长度。
		boolean hasWord = false;
		//	将找后继的单词从后面开始截,比如 找touch的后继,从单词后面截先解个h,然后遍历单词列表中以h开头的,若没有再截ch,找ch开头的,
		//	若还是没找到,至到截到ouch,因为题目要求不能有包含关系,所以不能截到0
		for(int j=preString.length()-1;j>0;j--) {
			int max=0;
			//	是否还需要再截的标志位,因为截的位数越少,所连接的龙就越长,所以只要第一次截出来的单词在单词列表中能匹配到,就不用往后再截。
			//	例如,cheat,第一次截t,,遍历发现有t开头的,这个时候我们没有必要继续往后截,因为cheat+touch→cheatouch,长度为两个单词长度减j,j越小,龙越长
			boolean preFlag=false;
			for(int i=0;i<words.length;i++) {
				int tmp=0;
				//	首先单词表中的长度要大与截的单词长度,其次判断单词表是否有开头为所截单词的单词,最后还得满足找到的单词的访问量小于2
					if(words[i].length()>preString.length()-j&&
							preString.substring(j, preString.length()).equals(words[i].substring(0, preString.length()-j))
							&&isVisited[i]<2) {
						//	如果以上条件满足,则表明已经找到了匹配单词,所以后面不用截了,标志位preFlag变为true
						preFlag = true;
						//	表示该单词后面还有单词
						hasWord = true;
						//	将该单词访问量加1
						isVisited[i]++;
						//	继续搜索后面的单词,并把连接最长的存入max,以touch为例,当截到ch时,遍历单词列表,第一次找到cheat,然后以cheat
						//	为下一个单词继续搜,并把长度记录到tmp中与max比较,然后遍历到单词choose发现也可以,然后以choose往下搜,最后将以
						//	cheat和choose往下搜中长度最长的返回。
						tmp = j+dfs(words, isVisited, words[i]);
						//	访问量恢复,为下一次搜索做准备
						isVisited[i]--;
						max = tmp>max?tmp:max;	
					}
								
			}
			//	preFlag为true时,表示不用往下截了,返回最大值即可
			if(preFlag)
				return max;
		}
		//	如果没进循环,表示找不到他的后继了,直接返回单词长度
		if (!hasWord) 
			return preString.length();
		return 0;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值