单词接龙的最短字符串长度:DFS加剪枝

前几天又刷到一单词接龙题,不一样的是这次要求的是能接龙的最短字符串长度,所谓能接龙的意思是最少有两个单词能够接龙(即单词一的尾字母与单词二的首字母一样),如果任意两个单词都无法接起来,输出0。这个题用DFS做是比较简单的,另外如果加些剪枝的话在大单词量的时候效率将会提升很多。

剪枝方法:首尾字母相同的单词在最短结果中最多只使用一次,因此对于首尾字母相同的单词,只记录最短单词的长度即可,其它单词可以扔掉。证明:假设最短结果存在两个首尾字母相同的单词  ...a->axb->bioyher->ra->ayyyb->b....那么必然可以把从第一个相同单词后面那个单词开始,到第二个相同单词结束的一段拿掉,变成...a->axb->b....,因此得证。但是存在一种特殊情况,比如说有下面一组单词: a  aa  word  god hello,显然最优结果是a->aa,首尾字母相同的单词出现了两次,所以对于首尾字母相同的单词,需要记录最短的两个单词的长度。

代码如下:


#include <iostream>

#define N 100
#define CHAR_CNT 26

static int minStrLen = INT32_MAX;
static int length[CHAR_CNT][CHAR_CNT];//下标0对应字母a,下标1对应字母b,依此类推,第一维代表首字母,第二维代表尾字母
static int special[CHAR_CNT][2];

int getLen(char *str, char *b, char *e) {
	*b = str[0];
	int i = 0;
	while (str[i] != '\0') {
		*e = str[i++];
	}
	return i;
}

void putWord() {
	for (int i = 0; i < N; i++) {
		char tmp[20], begin, end;
		int len, x, y;
		scanf("%s", tmp);
		len = getLen(tmp, &begin, &end);
		x = begin - 'a';
		y = end - 'a';
		if (length[x][y] == 0) {
			length[x][y] = len;
		}
		else {
			length[x][y] = len < length[x][y] ? len : length[x][y];
		}
		if (x == y) {//首尾字母相同的要记录最小的前两个单词长度
			if (special[x][0] == 0 || special[x][0] > len) {
				special[x][1] = special[x][0];
				special[x][0] = len;
			}
			else if (special[x][1] == 0 || special[x][1] > len) {
				special[x][1] = len;
			}
		}
	}
}

void dfs(int cur, int strLen, int wordCnt) {
	if (strLen >= minStrLen)//不可能比已有答案更优
		return;
	minStrLen = (strLen < minStrLen && wordCnt > 1) ? strLen : minStrLen;//至少要有两个单词
	for (int i = 0; i < CHAR_CNT; i++) {
		if (length[cur][i] > 0) {
			int tmp = length[cur][i];
			length[cur][i] = 0;
			dfs(i, strLen + tmp, wordCnt + 1);//当前尾字母作为首字母进行下一层的DFS
			length[cur][i] = tmp;
		}
	}
}

void checkSpecial() {
	for (int i = 0; i < CHAR_CNT; i++) {
		if (special[i][0] == 0 || special[i][1] == 0)
			continue;
		if (special[i][0] + special[i][1] < minStrLen)
			minStrLen = special[i][0] + special[i][1];
	}
}

int main()
{
	putWord();
	for (int i = 0; i < CHAR_CNT; i++) {//遍历26个字母作为有可能的龙头首字母
		dfs(i, 0, 0);
	}
	checkSpecial();
	if(minStrLen==INT32_MAX)
		std::cout << 0;
	else
		std::cout << minStrLen;
	return 0;
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热血大婶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值